cdbengine.cpp 60.8 KB
Newer Older
1
/**************************************************************************
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
27
**
28
**************************************************************************/
29

30
31
#include "cdbengine.h"
#include "cdbengine_p.h"
32
33
#include "cdbdebugoutput.h"
#include "cdbdebugeventcallback.h"
34
#include "cdbstacktracecontext.h"
35
#include "cdbsymbolgroupcontext.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
36
#include "cdbbreakpoint.h"
37
38
#include "cdbmodules.h"
#include "cdbassembler.h"
39
40
#include "cdboptionspage.h"
#include "cdboptions.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
41
#include "cdbexceptionutils.h"
42
#include "cdbsymbolpathlisteditor.h"
43
#include "debuggeragents.h"
Robert Loehning's avatar
Robert Loehning committed
44
45
#include "debuggeruiswitcher.h"
#include "debuggermainwindow.h"
Banana Joe's avatar
Banana Joe committed
46

47
#include "debuggeractions.h"
Banana Joe's avatar
Banana Joe committed
48
49
#include "breakhandler.h"
#include "stackhandler.h"
50
#include "watchhandler.h"
51
#include "threadshandler.h"
52
53
#include "registerhandler.h"
#include "moduleshandler.h"
54
#include "watchutils.h"
Banana Joe's avatar
Banana Joe committed
55

56
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
57
#include <utils/qtcassert.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
58
#include <utils/winutils.h>
59
#include <utils/consoleprocess.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
60
#include <utils/fancymainwindow.h>
61
#include <texteditor/itexteditor.h>
62
#include <utils/savedaction.h>
63
#include <utils/checkablemessagebox.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
64
#include <projectexplorer/toolchain.h>
hjk's avatar
hjk committed
65

Friedemann Kleint's avatar
Friedemann Kleint committed
66
#include <QtCore/QDebug>
67
#include <QtCore/QTimer>
Friedemann Kleint's avatar
Friedemann Kleint committed
68
69
70
#include <QtCore/QTimerEvent>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
Friedemann Kleint's avatar
Friedemann Kleint committed
71
#include <QtCore/QLibrary>
72
#include <QtCore/QCoreApplication>
73
74
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
75
#include <QtGui/QApplication>
76
#include <QtGui/QToolTip>
Banana Joe's avatar
Banana Joe committed
77
78

#define DBGHELP_TRANSLATE_TCHAR
79
#include <inc/Dbghelp.h>
Banana Joe's avatar
Banana Joe committed
80

81
82
83
84
85
static const char *localSymbolRootC = "local";

namespace Debugger {
namespace Internal {

86
CdbOptionsPage *theOptionsPage = 0;
Friedemann Kleint's avatar
Friedemann Kleint committed
87
88
typedef QList<WatchData> WatchList;

89
90
// ----- Message helpers

91
92
93
94
95
static QString msgStackIndexOutOfRange(int idx, int size)
{
    return QString::fromLatin1("Frame index %1 out of range (%2).").arg(idx).arg(size);
}

96
97
QString msgDebuggerCommandFailed(const QString &command, HRESULT hr)
{
98
    return QString::fromLatin1("Unable to execute '%1': %2").arg(command, CdbCore::msgDebugEngineComResult(hr));
99
100
}

101
static const char *msgNoStackTraceC = "Internal error: no stack trace present.";
Friedemann Kleint's avatar
Friedemann Kleint committed
102

103
104
105
106
107
108
109
110
111
112
113
114
115
// Format function failure message. Pass in Q_FUNC_INFO
static QString msgFunctionFailed(const char *func, const QString &why)
{
    // Strip a "cdecl_ int namespace1::class::foo(int bar)" as
    // returned by Q_FUNC_INFO down to "foo"
    QString function = QLatin1String(func);
    const int firstParentPos = function.indexOf(QLatin1Char('('));
    if (firstParentPos != -1)
        function.truncate(firstParentPos);
    const int classSepPos = function.lastIndexOf(QLatin1String("::"));
    if (classSepPos != -1)
        function.remove(0, classSepPos + 2);
   //: Function call failed
116
   return CdbEngine::tr("The function \"%1()\" failed: %2").arg(function, why);
117
118
}

119
120
// ----- Engine helpers

121
// --- CdbEnginePrivate
Friedemann Kleint's avatar
Friedemann Kleint committed
122

123
CdbEnginePrivate::CdbEnginePrivate(CdbEngine *engine) :
124
    m_options(theOptionsPage->options()),
Banana Joe's avatar
Banana Joe committed
125
126
    m_hDebuggeeProcess(0),
    m_hDebuggeeThread(0),
127
    m_breakEventMode(BreakEventHandle),
Robert Loehning's avatar
Robert Loehning committed
128
    m_dumper(new CdbDumperHelper(engine, this)),
129
130
    m_currentThreadId(-1),
    m_eventThreadId(-1),
Friedemann Kleint's avatar
Friedemann Kleint committed
131
    m_interruptArticifialThreadId(-1),
132
    m_ignoreInitialBreakPoint(false),
133
    m_interrupted(false),
Friedemann Kleint's avatar
Friedemann Kleint committed
134
    m_engine(engine),
135
    m_currentStackTrace(0),
Friedemann Kleint's avatar
Friedemann Kleint committed
136
    m_firstActivatedFrame(true),
Friedemann Kleint's avatar
Friedemann Kleint committed
137
    m_inferiorStartupComplete(false),
138
139
    m_mode(AttachCore),
    m_stoppedReason(StoppedOther)
140
{
141
    connect(this, SIGNAL(watchTimerDebugEvent()), this, SLOT(handleDebugEvent()));
142
    connect(this, SIGNAL(modulesLoaded()), this, SLOT(slotModulesLoaded()));
143
144
}

145
bool CdbEnginePrivate::init(QString *errorMessage)
Banana Joe's avatar
Banana Joe committed
146
{
147
    enum {  bufLen = 10240 };
148

149
    if (!CdbCore::CoreEngine::init(m_options->path, errorMessage))
150
        return false;
151
152
153
154
    CdbDebugOutput *output = new CdbDebugOutput;
    connect(output, SIGNAL(showMessage(QString,int,int)),
            m_engine, SLOT(showMessage(QString,int,int)),
            Qt::QueuedConnection); // Multithread invocation when loading dumpers.
155
156
157
    setDebugOutput(DebugOutputBasePtr(output));
    setDebugEventCallback(DebugEventCallbackBasePtr(new CdbDebugEventCallback(m_engine)));
    updateCodeLevel();
158

159
    return true;
Friedemann Kleint's avatar
Friedemann Kleint committed
160
}
Banana Joe's avatar
Banana Joe committed
161

162
DebuggerEngine *CdbEngine::create(const DebuggerStartParameters &sp,
163
                                       QString *errorMessage)
Friedemann Kleint's avatar
Friedemann Kleint committed
164
{
165
    CdbEngine *rc = new CdbEngine(sp);
166
167
    if (rc->m_d->init(errorMessage)) {
        rc->syncDebuggerPaths();
168
        return rc;
169
    }
170
171
    delete rc;
    return 0;
Banana Joe's avatar
Banana Joe committed
172
173
}

174
void  CdbEnginePrivate::updateCodeLevel()
175
{
176
177
178
    const CdbCore::CoreEngine::CodeLevel cl = theDebuggerBoolSetting(OperateByInstruction) ?
                                              CdbCore::CoreEngine::CodeLevelAssembly : CdbCore::CoreEngine::CodeLevelSource;
    setCodeLevel(cl);
179
180
}

181
CdbEnginePrivate::~CdbEnginePrivate()
Banana Joe's avatar
Banana Joe committed
182
{
183
    cleanStackTrace();
Banana Joe's avatar
Banana Joe committed
184
185
}

186
void CdbEnginePrivate::clearForRun()
187
188
189
190
{
    if (debugCDB)
        qDebug() << Q_FUNC_INFO;

191
    m_breakEventMode = BreakEventHandle;
Friedemann Kleint's avatar
Friedemann Kleint committed
192
    m_eventThreadId = m_interruptArticifialThreadId = -1;
193
    m_interrupted = false;
194
    cleanStackTrace();
195
196
    m_stoppedReason = StoppedOther;
    m_stoppedMessage.clear();
197
    m_engine->threadsHandler()->notifyRunning();
198
199
}

200
void CdbEnginePrivate::cleanStackTrace()
201
{
202
203
204
205
    if (m_currentStackTrace) {
        delete m_currentStackTrace;
        m_currentStackTrace = 0;
    }
206
207
    m_firstActivatedFrame = false;
    m_editorToolTipCache.clear();
208
209
}

210
CdbEngine::CdbEngine(const DebuggerStartParameters &startParameters) :
Friedemann Kleint's avatar
Friedemann Kleint committed
211
    DebuggerEngine(startParameters),
212
    m_d(new CdbEnginePrivate(this))
213
{
214
    m_d->m_consoleStubProc.setMode(Utils::ConsoleProcess::Suspend);
215
216
    connect(&m_d->m_consoleStubProc, SIGNAL(processMessage(QString,bool)),
            this, SLOT(slotConsoleStubMessage(QString, bool)));
hjk's avatar
hjk committed
217
218
219
220
    connect(&m_d->m_consoleStubProc, SIGNAL(processStarted()),
            this, SLOT(slotConsoleStubStarted()));
    connect(&m_d->m_consoleStubProc, SIGNAL(wrapperStopped()),
            this, SLOT(slotConsoleStubTerminated()));
221
222
}

223
CdbEngine::~CdbEngine()
224
225
226
227
{
    delete m_d;
}

228
void CdbEngine::setState(DebuggerState state, const char *func, int line)
229
230
231
{
    if (debugCDB)
        qDebug() << "setState(" << state << ") at " << func << ':' << line;
232
    DebuggerEngine::setState(state);
233
234
}

235
void CdbEngine::shutdown()
Banana Joe's avatar
Banana Joe committed
236
237
238
239
{
    exitDebugger();
}

240
QString CdbEngine::editorToolTip(const QString &exp, const QString &function)
Banana Joe's avatar
Banana Joe committed
241
{
242
243
244
245
246
247
    // Figure the editor tooltip. Ask the frame context of the
    // function if it is a local variable it knows. If that is not
    // the case, try to evaluate via debugger
    QString errorMessage;
    QString rc;
    // Find the frame of the function if there is any
248
    CdbSymbolGroupContext *frame = 0;
249
250
    if (m_d->m_currentStackTrace &&  !function.isEmpty()) {
        const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
251
        if (debugToolTips)
252
            qDebug() << "CdbEngine::editorToolTip" << exp << function << frameIndex;
253
        if (frameIndex != -1)
254
            frame = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
255
    }
256
257
    if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
        return rc;
258
259
260
    // No function/symbol context found, try to evaluate in current context.
    // Do not append type as this will mostly be 'long long' for integers, etc.
    QString type;
261
262
    if (debugToolTips)
        qDebug() << "Defaulting to expression";
263
    if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage))
264
265
266
267
        return QString();
    return rc;
}

268
void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
269
{
270
    typedef CdbEnginePrivate::EditorToolTipCache EditorToolTipCache;
271
    if (debugCDB)
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
        qDebug() << Q_FUNC_INFO << '\n' << cursorPos;
    // Need a stopped debuggee and a cpp file
    if (!m_d->m_hDebuggeeProcess || m_d->isDebuggeeRunning())
        return;
    if (!isCppEditor(editor))
        return;
    // Determine expression and function
    QString toolTip;
    do {
        int line;
        int column;
        QString function;
        const QString exp = cppExpressionAt(editor, cursorPos, &line, &column, &function);
        if (function.isEmpty() || exp.isEmpty())
            break;
        // Check cache (key containing function) or try to figure out expression
        QString cacheKey = function;
        cacheKey += QLatin1Char('@');
        cacheKey += exp;
        const EditorToolTipCache::const_iterator cit = m_d->m_editorToolTipCache.constFind(cacheKey);
        if (cit != m_d->m_editorToolTipCache.constEnd()) {
            toolTip = cit.value();
        } else {
            toolTip = editorToolTip(exp, function);
            if (!toolTip.isEmpty())
                m_d->m_editorToolTipCache.insert(cacheKey, toolTip);
        }
    } while (false);
    // Display
    QToolTip::hideText();
    if (!toolTip.isEmpty())
        QToolTip::showText(mousePos, toolTip);
Banana Joe's avatar
Banana Joe committed
304
305
}

306
void CdbEnginePrivate::clearDisplay()
307
{
308
309
310
    m_engine->threadsHandler()->removeAll();
    m_engine->modulesHandler()->removeAll();
    m_engine->registerHandler()->removeAll();
311
312
}

313
void CdbEnginePrivate::checkVersion()
314
315
316
317
318
319
320
{
    static bool versionNotChecked = true;
    // Check for version 6.11 (extended expression syntax)
    if (versionNotChecked) {
        versionNotChecked = false;
        // Get engine DLL version
        QString errorMessage;
321
        const QString version = Utils::winGetDLLVersion(Utils::WinDLLProductVersion, dbengDLL(), &errorMessage);
322
323
324
325
326
327
        if (version.isEmpty()) {
            qWarning("%s\n", qPrintable(errorMessage));
            return;
        }
        // Compare
        const double minVersion = 6.11;
328
        m_engine->showMessage(CdbEngine::tr("Version: %1").arg(version));
329
        if (version.toDouble() <  minVersion) {
330
            const QString msg = CdbEngine::tr(
331
332
333
                    "<html>The installed version of the <i>Debugging Tools for Windows</i> (%1) "
                    "is rather old. Upgrading to version %2 is recommended "
                    "for the proper display of Qt's data types.</html>").arg(version).arg(minVersion);
334
            Core::ICore::instance()->showWarningWithOptions(CdbEngine::tr("Debugger"), msg, QString(),
335
336
337
338
339
340
                                                            QLatin1String(Constants::DEBUGGER_SETTINGS_CATEGORY),
                                                            CdbOptionsPage::settingsId());
        }
    }
}

341
void CdbEngine::startupChecks()
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
{
    // Check symbol server unless the user has an external/internal setup
    if (!qgetenv("_NT_SYMBOL_PATH").isEmpty()
        || CdbOptions::indexOfSymbolServerPath(m_d->m_options->symbolPaths) != -1)
        return;
    // Prompt to use Symbol server unless the user checked "No nagging".
    Core::ICore *core = Core::ICore::instance();
    const QString nagSymbolServerKey = CdbOptions::settingsGroup() + QLatin1String("/NoPromptSymbolServer");
    bool noFurtherNagging = core->settings()->value(nagSymbolServerKey, false).toBool();
    if (noFurtherNagging)
        return;

    const QString symServUrl = QLatin1String("http://support.microsoft.com/kb/311503");
    const QString msg = tr("<html><head/><body><p>The debugger is not configured to use the public "
                           "<a href=\"%1\">Microsoft Symbol Server</a>. This is recommended "
                           "for retrieval of the symbols of the operating system libraries.</p>"
                           "<p><i>Note:</i> A fast internet connection is required for this to work smoothly. Also, a delay "
                           "might occur when connecting for the first time.</p>"
                           "<p>Would you like to set it up?</p></br>"
                           "</body></html>").arg(symServUrl);
    const QDialogButtonBox::StandardButton answer =
            Utils::CheckableMessageBox::question(core->mainWindow(), tr("Symbol Server"), msg,
                                                 tr("Do not ask again"), &noFurtherNagging);
    core->settings()->setValue(nagSymbolServerKey, noFurtherNagging);
    if (answer == QDialogButtonBox::No)
        return;
    // Prompt for path and add it. Synchronize QSetting and debugger.
    const QString cacheDir = CdbSymbolPathListEditor::promptCacheDirectory(core->mainWindow());
    if (cacheDir.isEmpty())
        return;
    m_d->m_options->symbolPaths.push_back(CdbOptions::symbolServerPath(cacheDir));
    m_d->m_options->toSettings(core->settings());
    syncDebuggerPaths();
}

hjk's avatar
hjk committed
377
void CdbEngine::setupEngine()
378
{
379
    QTC_ASSERT(state() == EngineSettingUp, qDebug() << state());
Friedemann Kleint's avatar
Friedemann Kleint committed
380
    const DebuggerStartParameters &sp = startParameters();
Friedemann Kleint's avatar
Friedemann Kleint committed
381
    if (debugCDBExecution)
382
        qDebug() << "startDebugger";
383
    CdbCore::BreakPoint::clearNormalizeFileNameCache();
384
    startupChecks();
385
    m_d->checkVersion();
386
387
    if (m_d->m_hDebuggeeProcess) {
        warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged."));
388
        notifyEngineSetupFailed();
389
        return;
390
    }
391
    switch (sp.startMode) {
392
    case AttachCore:
ck's avatar
ck committed
393
    case AttachToRemote:
394
        warning(QLatin1String("Internal error: Mode not supported."));
395
        notifyEngineSetupFailed();
396
397
398
399
        break;
    default:
        break;
    }
400
    m_d->m_mode = sp.startMode;
401
    m_d->clearDisplay();
Friedemann Kleint's avatar
Friedemann Kleint committed
402
    m_d->m_inferiorStartupComplete = false;
403
404
405
    // Options
    QString errorMessage;
    if (!m_d->setBreakOnThrow(theDebuggerBoolSetting(BreakOnThrow), &errorMessage))
hjk's avatar
hjk committed
406
        showMessage(errorMessage, LogWarning);
407
    m_d->setVerboseSymbolLoading(m_d->m_options->verboseSymbolLoading);
408
    // Figure out dumper. @TODO: same in gdb...
409
    const QString dumperLibName = QDir::toNativeSeparators(qtDumperLibraryName());
410
411
    bool dumperEnabled = m_d->m_mode != AttachCore
                         && m_d->m_mode != AttachCrashedExternal
412
                         && qtDumperLibraryEnabled();
413
414
415
    if (dumperEnabled) {
        const QFileInfo fi(dumperLibName);
        if (!fi.isFile()) {
416
            const QStringList &locations = qtDumperLibraryLocations();
417
418
            const QString loc = locations.join(QLatin1String(", "));
            const QString msg = tr("The dumper library was not found at %1.").arg(loc);
419
            showQtDumperLibraryWarning(msg);
420
421
422
            dumperEnabled = false;
        }
    }
423
    m_d->m_dumper->reset(dumperLibName, dumperEnabled);
424
    notifyEngineSetupOk();
hjk's avatar
hjk committed
425
}
426

hjk's avatar
hjk committed
427
428
429
430
431
void CdbEngine::setupInferior()
{
    QTC_ASSERT(state() == InferiorSettingUp, qDebug() << state());
    notifyInferiorSetupOk();
}
432

hjk's avatar
hjk committed
433
434
435
436
void CdbEngine::runEngine()
{
    QTC_ASSERT(m_state == InferiorSetupOk, qDebug() << m_state);
    showStatusMessage("Starting Debugger", messageTimeOut);
Friedemann Kleint's avatar
Friedemann Kleint committed
437
    bool rc = false;
438
    bool needWatchTimer = false;
439
    m_d->clearForRun();
440
    m_d->updateCodeLevel();
441
    m_d->m_ignoreInitialBreakPoint = false;
442
    switch (m_d->m_mode) {
Friedemann Kleint's avatar
Friedemann Kleint committed
443
    case AttachExternal:
444
    case AttachCrashedExternal:
445
        rc = startAttachDebugger(sp.attachPID, m_d->m_mode, &errorMessage);
446
        needWatchTimer = true; // Fetch away module load, etc. even if crashed
Friedemann Kleint's avatar
Friedemann Kleint committed
447
448
449
        break;
    case StartInternal:
    case StartExternal:
450
        if (sp.useTerminal) {
451
452
            // Attaching to console processes triggers an initial breakpoint, which we do not want
            m_d->m_ignoreInitialBreakPoint = true;
453
454
            // Launch console stub and wait for its startup
            m_d->m_consoleStubProc.stop(); // We leave the console open, so recycle it now.
455
456
457
            m_d->m_consoleStubProc.setWorkingDirectory(sp.workingDirectory);
            m_d->m_consoleStubProc.setEnvironment(sp.environment);
            rc = m_d->m_consoleStubProc.start(sp.executable, sp.processArgs);
458
            if (!rc)
459
                errorMessage = tr("The console stub process was unable to start '%1'.").arg(sp.executable);
460
            // continues in slotConsoleStubStarted()...
461
        } else {
462
            needWatchTimer = true;
463
464
465
466
            rc = m_d->startDebuggerWithExecutable(sp.workingDirectory,
                                                  sp.executable,
                                                  sp.processArgs,
                                                  sp.environment,
467
                                                  &errorMessage);
468
        }
Friedemann Kleint's avatar
Friedemann Kleint committed
469
470
        break;
    case AttachCore:
Friedemann Kleint's avatar
Friedemann Kleint committed
471
        errorMessage = tr("Attaching to core files is not supported!");
Friedemann Kleint's avatar
Friedemann Kleint committed
472
473
474
        break;
    }
    if (rc) {
475
        if (needWatchTimer)
476
            m_d->startWatchTimer();
hjk's avatar
hjk committed
477
        notifyInferiorSetupOk();
Friedemann Kleint's avatar
Friedemann Kleint committed
478
    } else {
479
        warning(errorMessage);
hjk's avatar
hjk committed
480
        notifyInferiorSetupFailed();
481
    }
Friedemann Kleint's avatar
Friedemann Kleint committed
482
}
Banana Joe's avatar
Banana Joe committed
483

484
bool CdbEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage)
Friedemann Kleint's avatar
Friedemann Kleint committed
485
{
486
    // Need to attach invasively, otherwise, no notification signals
487
    // for for CreateProcess/ExitProcess occur.
488
489
490
491
492
493
494
495
    // Initial breakpoint occur:
    // 1) Desired: When attaching to a crashed process
    // 2) Undesired: When starting up a console process, in conjunction
    //    with the 32bit Wow-engine
    //  As of version 6.11, the flag only affects 1). 2) Still needs to be suppressed
    // by lookup at the state of the application (startup trap). However,
    // there is no startup trap when attaching to a process that has been
    // running for a while. (see notifyException).
496
    const bool suppressInitialBreakPoint = sm != AttachCrashedExternal;
497
    return m_d->startAttachDebugger(pid, suppressInitialBreakPoint, errorMessage);
Banana Joe's avatar
Banana Joe committed
498
499
}

500
void CdbEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle)
501
{
502
    m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__);
503
504
    setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle));
    ULONG currentThreadId;
505
    if (SUCCEEDED(interfaces().debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, &currentThreadId))) {
506
507
508
509
        m_currentThreadId = currentThreadId;
    } else {
        m_currentThreadId = 0;
    }
510
511
    // Clear any saved breakpoints and set initial breakpoints
    m_engine->executeDebuggerCommand(QLatin1String("bc"));
512
    if (m_engine->breakHandler()->hasPendingBreakpoints()) {
Friedemann Kleint's avatar
Friedemann Kleint committed
513
514
        if (debugCDBExecution)
            qDebug() << "processCreatedAttached: Syncing breakpoints";
515
        m_engine->attemptBreakpointSynchronization();
Friedemann Kleint's avatar
Friedemann Kleint committed
516
    }
517
518
    // Attaching to crashed: This handshake (signalling an event) is required for
    // the exception to be delivered to the debugger
519
    // Also, see special handling in slotModulesLoaded().
520
    if (m_mode == AttachCrashedExternal) {
Friedemann Kleint's avatar
Friedemann Kleint committed
521
        const QString crashParameter = m_engine->startParameters().crashParameter;
522
523
        if (!crashParameter.isEmpty()) {
            ULONG64 evtNr = crashParameter.toULongLong();
524
            const HRESULT hr = interfaces().debugControl->SetNotifyEventHandle(evtNr);
525
            if (FAILED(hr))
526
                m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(CdbCore::msgComFailed("SetNotifyEventHandle", hr)));
527
528
        }
    }
529
    m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
Friedemann Kleint's avatar
Friedemann Kleint committed
530
    if (debugCDBExecution)
531
        qDebug() << "<processCreatedAttached";
532
533
}

534
void CdbEngine::processTerminated(unsigned long exitCode)
535
{
hjk's avatar
hjk committed
536
    showMessage(tr("The process exited with exit code %1.").arg(exitCode));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
537
    if (state() != InferiorStopping)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
538
539
        setState(InferiorStopping, Q_FUNC_INFO, __LINE__);
    setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
540
    setState(InferiorShuttingDown, Q_FUNC_INFO, __LINE__);
541
    m_d->setDebuggeeHandles(0, 0);
542
543
544
    m_d->clearForRun();
    setState(InferiorShutDown, Q_FUNC_INFO, __LINE__);
    // Avoid calls from event handler.
545
    QTimer::singleShot(0, this, SLOT(quitDebugger()));
546
547
}

548
bool CdbEnginePrivate::endInferior(EndInferiorAction action, QString *errorMessage)
Banana Joe's avatar
Banana Joe committed
549
{
550
551
    // Process must be stopped in order to terminate
    m_engine->setState(InferiorShuttingDown, Q_FUNC_INFO, __LINE__); // pretend it is shutdown
Friedemann Kleint's avatar
Friedemann Kleint committed
552
    const bool wasRunning = isDebuggeeRunning();
553
554
    if (wasRunning) {
        interruptInterferiorProcess(errorMessage);
Friedemann Kleint's avatar
Friedemann Kleint committed
555
        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
556
    }
557
    bool success = false;
Friedemann Kleint's avatar
Friedemann Kleint committed
558
    switch (action) {
559
560
    case DetachInferior:
            if (detachCurrentProcess(errorMessage))
561
562
                success = true;
            break;
563
    case TerminateInferior:
564
565
            do {
                // The exit process event handler will not be called.
566
                terminateCurrentProcess(errorMessage);
567
568
569
570
                if (wasRunning) {
                    success = true;
                    break;
                }
571
                if (terminateProcesses(errorMessage))
572
573
574
575
                    success = true;
            } while (false);
            QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
            break;
Friedemann Kleint's avatar
Friedemann Kleint committed
576
    }
577
    // Perform cleanup even when failed..no point clinging to the process
Friedemann Kleint's avatar
Friedemann Kleint committed
578
    setDebuggeeHandles(0, 0);
579
    killWatchTimer();
580
581
582
    m_engine->setState(success ? InferiorShutDown : InferiorShutdownFailed, Q_FUNC_INFO, __LINE__);
    return success;
}
583

584
585
586
// End debugging. Note that this can invoked via user action
// or the processTerminated() event handler, in which case it
// must not kill the process again.
587
void CdbEnginePrivate::endDebugging(EndDebuggingMode em)
588
589
590
{
    if (debugCDB)
        qDebug() << Q_FUNC_INFO << em;
591

592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
    const DebuggerState oldState = m_engine->state();
    if (oldState == DebuggerNotReady || m_mode == AttachCore)
        return;
    // Do we need to stop the process?
    QString errorMessage;
    if (oldState != InferiorShutDown && m_hDebuggeeProcess) {
        EndInferiorAction action;
        switch (em) {
        case EndDebuggingAuto:
            action = (m_mode == AttachExternal || m_mode == AttachCrashedExternal) ?
                     DetachInferior : TerminateInferior;
            break;
        case EndDebuggingDetach:
            action = DetachInferior;
            break;
        case EndDebuggingTerminate:
            action = TerminateInferior;
            break;
        }
        if (debugCDB)
            qDebug() << Q_FUNC_INFO << action;
        // Need a stopped debuggee to act
        if (!endInferior(action, &errorMessage)) {
            errorMessage = QString::fromLatin1("Unable to detach from/end the debuggee: %1").arg(errorMessage);
Robert Loehning's avatar
Robert Loehning committed
616
            m_engine->showMessage(errorMessage, LogError);
617
618
619
620
        }
        errorMessage.clear();
    }
    // Clean up resources (open files, etc.)
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
621
    m_engine->setState(EngineShuttingDown, Q_FUNC_INFO, __LINE__);
622
    clearForRun();
623
    const bool endedCleanly = endSession(&errorMessage);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
624
    m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__);
625
626
    if (!endedCleanly) {
        errorMessage = QString::fromLatin1("There were errors trying to end debugging:\n%1").arg(errorMessage);
Robert Loehning's avatar
Robert Loehning committed
627
        m_engine->showMessage(errorMessage, LogError);
628
    }
Friedemann Kleint's avatar
Friedemann Kleint committed
629
630
}

631
void CdbEngine::exitDebugger()
Friedemann Kleint's avatar
Friedemann Kleint committed
632
633
634
635
{
    m_d->endDebugging();
}

636
void CdbEngine::detachDebugger()
Friedemann Kleint's avatar
Friedemann Kleint committed
637
{
638
    m_d->endDebugging(CdbEnginePrivate::EndDebuggingDetach);
Banana Joe's avatar
Banana Joe committed
639
640
}

641
CdbSymbolGroupContext *CdbEnginePrivate::getSymbolGroupContext(int frameIndex, QString *errorMessage) const
642
643
644
645
646
{
    if (!m_currentStackTrace) {
        *errorMessage = QLatin1String(msgNoStackTraceC);
        return 0;
    }
647
    if (CdbSymbolGroupContext *sg = m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, errorMessage))
648
649
650
651
        return sg;
    return 0;
}

652
void CdbEngine::evaluateWatcher(WatchData *wd)
653
654
655
656
657
658
{
    if (debugCDBWatchHandling)
        qDebug() << Q_FUNC_INFO << wd->exp;
    QString errorMessage;
    QString value;
    QString type;
659
660
661
662
663
    QString exp = wd->exp;
    // Remove locals watch prefix.
    if (exp.startsWith(QLatin1String("local.")))
        exp.remove(0, 6);
    if (m_d->evaluateExpression(exp, &value, &type, &errorMessage)) {
664
665
666
667
668
669
670
671
672
        wd->setValue(value);
        wd->setType(type);
    } else {
        wd->setValue(errorMessage);
        wd->setTypeUnneeded();
    }
    wd->setHasChildren(false);
}

673
void CdbEngine::updateWatchData(const WatchData &incomplete)
674
675
676
677
678
679
680
681
{
    // Watch item was edited while running
    if (m_d->isDebuggeeRunning())
        return;

    if (debugCDBWatchHandling)
        qDebug() << Q_FUNC_INFO << "\n    " << incomplete.toString();

682
    if (incomplete.iname.startsWith("watch.")) {
683
684
        WatchData watchData = incomplete;
        evaluateWatcher(&watchData);
685
        watchHandler()->insertData(watchData);
686
687
688
        return;
    }

689
    const int frameIndex = stackHandler()->currentIndex();
690
691
692
693

    bool success = false;
    QString errorMessage;
    do {
694
        CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
695
696
        if (!sg)
            break;
697
        if (!sg->completeData(incomplete, watchHandler(), &errorMessage))
698
699
700
701
702
            break;
        success = true;
    } while (false);
    if (!success)
        warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
703
    if (debugCDBWatchHandling > 1)
704
        qDebug() << *watchHandler()->model(LocalsWatch);
705
706
}

707
708
// Continue inferior with a debugger command, such as "p", "pt"
// or its thread variations
709
bool CdbEnginePrivate::executeContinueCommand(const QString &command)
710
711
712
713
{
    if (debugCDB)
        qDebug() << Q_FUNC_INFO << command;
    clearForRun();
714
    updateCodeLevel(); // Step by instruction
715
    m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__);
716
    m_engine->showMessage(CdbEngine::tr("Continuing with '%1'...").arg(command));
717
    QString errorMessage;
718
    const bool success = executeDebuggerCommand(command, &errorMessage);
719
720
    if (success) {
        m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
721
        startWatchTimer();
722
723
    } else {
        m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
724
        m_engine->warning(CdbEngine::tr("Unable to continue: %1").arg(errorMessage));
725
726
727
728
    }
    return success;
}

729
730
731
732
733
734
735
static inline QString msgStepFailed(unsigned long executionStatus, int threadId, const QString &why)
{
    if (executionStatus ==  DEBUG_STATUS_STEP_OVER)
        return QString::fromLatin1("Thread %1: Unable to step over: %2").arg(threadId).arg(why);
    return QString::fromLatin1("Thread %1: Unable to step into: %2").arg(threadId).arg(why);
}

Friedemann Kleint's avatar
Friedemann Kleint committed
736
737
738
739
740
741
// Step out has to be done via executing 'gu'. TODO: Remove once it is
// accessible via normal API for SetExecutionStatus().

enum { CdbExtendedExecutionStatusStepOut = 7452347 };

// Step with  DEBUG_STATUS_STEP_OVER ('p'-command),
742
// DEBUG_STATUS_STEP_INTO ('t'-trace-command) or
Friedemann Kleint's avatar
Friedemann Kleint committed
743
// CdbExtendedExecutionStatusStepOut ("gu"-command)
744
// its reverse equivalents in the case of single threads.
Friedemann Kleint's avatar
Friedemann Kleint committed
745

746
bool CdbEngine::step(unsigned long executionStatus)
Banana Joe's avatar
Banana Joe committed
747
{
Friedemann Kleint's avatar
Friedemann Kleint committed
748
749
    if (debugCDBExecution)
        qDebug() << ">step" << executionStatus << "curr " << m_d->m_currentThreadId << " evt " << m_d->m_eventThreadId;
Friedemann Kleint's avatar
Friedemann Kleint committed
750

751
752
753
754
755
756
757
758
    // State of reverse stepping as of 10/2009 (Debugging tools 6.11@404):
    // The constants exist, but invoking the calls leads to E_NOINTERFACE.
    // Also there is no CDB command for it.
    if (executionStatus == DEBUG_STATUS_REVERSE_STEP_OVER || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO) {
        warning(tr("Reverse stepping is not implemented."));
        return false;
    }

Friedemann Kleint's avatar
Friedemann Kleint committed
759
760
761
762
763
764
    // Do not step the artifical thread created to interrupt the debuggee.
    if (m_d->m_interrupted && m_d->m_currentThreadId == m_d->m_interruptArticifialThreadId) {
        warning(tr("Thread %1 cannot be stepped.").arg(m_d->m_currentThreadId));
        return false;
    }

765
766
767
768
769
770
771
    // SetExecutionStatus() continues the thread that triggered the
    // stop event (~# p). This can be confusing if the user is looking
    // at the stack trace of another thread and wants to step that one. If that
    // is the case, explicitly tell it to step the current thread using a command.
    const int triggeringEventThread = m_d->m_eventThreadId;
    const bool sameThread = triggeringEventThread == -1
                            || m_d->m_currentThreadId == triggeringEventThread
772
                            || threadsHandler()->threads().size() == 1;
773
    m_d->clearForRun(); // clears thread ids
774
    m_d->updateCodeLevel(); // Step by instruction or source line
775
    setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__);
776
    bool success = false;
Friedemann Kleint's avatar
Friedemann Kleint committed
777
    if (sameThread && executionStatus != CdbExtendedExecutionStatusStepOut) { // Step event-triggering thread, use fast API
778
        const HRESULT hr = m_d->interfaces().debugControl->SetExecutionStatus(executionStatus);
779
780
        success = SUCCEEDED(hr);
        if (!success)
781
            warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, CdbCore::msgComFailed("SetExecutionStatus", hr)));
782
783
784
785
    } else {
        // Need to use a command to explicitly specify the current thread
        QString command;
        QTextStream str(&command);
Friedemann Kleint's avatar
Friedemann Kleint committed
786
787
788
789
790
791
        str << '~' << m_d->m_currentThreadId << ' ';
        switch (executionStatus) {
        case DEBUG_STATUS_STEP_OVER:
            str << 'p';
            break;
        case DEBUG_STATUS_STEP_INTO:
792
            str << 't';
Friedemann Kleint's avatar
Friedemann Kleint committed
793
794
795
796
797
            break;
        case CdbExtendedExecutionStatusStepOut:
            str << "gu";
            break;
        }
hjk's avatar
hjk committed
798
        showMessage(tr("Stepping %1").arg(command));
799
        const HRESULT hr = m_d->interfaces().debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, command.toLatin1().constData(), DEBUG_EXECUTE_ECHO);
800
801
802
803
804
        success = SUCCEEDED(hr);
        if (!success)
            warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, msgDebuggerCommandFailed(command, hr)));
    }
    if (success) {
805
806
        // Oddity: Step into will first break at the calling function. Ignore
        if (executionStatus == DEBUG_STATUS_STEP_INTO || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO)
807
            m_d->m_breakEventMode = CdbEnginePrivate::BreakEventIgnoreOnce;
808
        m_d->startWatchTimer();
809
810
811
812
        setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
    } else {
        setState(InferiorStopped, Q_FUNC_INFO