1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "cdbengine.h"
31 #include "cdbengine_p.h"
32 #include "cdbdebugoutput.h"
33 #include "cdbdebugeventcallback.h"
34 #include "cdbstacktracecontext.h"
35 #include "cdbsymbolgroupcontext.h"
36 #include "cdbbreakpoint.h"
37 #include "cdbmodules.h"
38 #include "cdbassembler.h"
39 #include "cdboptionspage.h"
40 #include "cdboptions.h"
41 #include "cdbexceptionutils.h"
42 #include "cdbsymbolpathlisteditor.h"
43 #include "debuggeragents.h"
44 #include "debuggeruiswitcher.h"
45 #include "debuggermainwindow.h"
47 #include "debuggeractions.h"
48 #include "breakhandler.h"
49 #include "stackhandler.h"
50 #include "watchhandler.h"
51 #include "threadshandler.h"
52 #include "registerhandler.h"
53 #include "moduleshandler.h"
54 #include "watchutils.h"
56 #include <coreplugin/icore.h>
57 #include <utils/qtcassert.h>
58 #include <utils/winutils.h>
59 #include <utils/consoleprocess.h>
60 #include <utils/fancymainwindow.h>
61 #include <texteditor/itexteditor.h>
62 #include <utils/savedaction.h>
63 #include <utils/checkablemessagebox.h>
64 #include <projectexplorer/toolchain.h>
66 #include <QtCore/QDebug>
67 #include <QtCore/QTimer>
68 #include <QtCore/QTimerEvent>
69 #include <QtCore/QFileInfo>
70 #include <QtCore/QDir>
71 #include <QtCore/QLibrary>
72 #include <QtCore/QCoreApplication>
73 #include <QtGui/QMessageBox>
74 #include <QtGui/QMainWindow>
75 #include <QtGui/QApplication>
76 #include <QtGui/QToolTip>
78 #define DBGHELP_TRANSLATE_TCHAR
79 #include <inc/Dbghelp.h>
81 static const char *localSymbolRootC = "local";
86 CdbOptionsPage *theOptionsPage = 0;
87 typedef QList<WatchData> WatchList;
89 // ----- Message helpers
91 static QString msgStackIndexOutOfRange(int idx, int size)
93 return QString::fromLatin1("Frame index %1 out of range (%2).").arg(idx).arg(size);
96 QString msgDebuggerCommandFailed(const QString &command, HRESULT hr)
98 return QString::fromLatin1("Unable to execute '%1': %2").arg(command, CdbCore::msgDebugEngineComResult(hr));
101 static const char *msgNoStackTraceC = "Internal error: no stack trace present.";
103 // Format function failure message. Pass in Q_FUNC_INFO
104 static QString msgFunctionFailed(const char *func, const QString &why)
106 // Strip a "cdecl_ int namespace1::class::foo(int bar)" as
107 // returned by Q_FUNC_INFO down to "foo"
108 QString function = QLatin1String(func);
109 const int firstParentPos = function.indexOf(QLatin1Char('('));
110 if (firstParentPos != -1)
111 function.truncate(firstParentPos);
112 const int classSepPos = function.lastIndexOf(QLatin1String("::"));
113 if (classSepPos != -1)
114 function.remove(0, classSepPos + 2);
115 //: Function call failed
116 return CdbEngine::tr("The function \"%1()\" failed: %2").arg(function, why);
119 // ----- Engine helpers
121 // --- CdbEnginePrivate
123 CdbEnginePrivate::CdbEnginePrivate(CdbEngine *engine) :
124 m_options(theOptionsPage->options()),
125 m_hDebuggeeProcess(0),
126 m_hDebuggeeThread(0),
127 m_breakEventMode(BreakEventHandle),
128 m_dumper(new CdbDumperHelper(engine, this)),
129 m_currentThreadId(-1),
131 m_interruptArticifialThreadId(-1),
132 m_ignoreInitialBreakPoint(false),
133 m_interrupted(false),
135 m_currentStackTrace(0),
136 m_firstActivatedFrame(true),
137 m_inferiorStartupComplete(false),
139 m_stoppedReason(StoppedOther)
141 connect(this, SIGNAL(watchTimerDebugEvent()), this, SLOT(handleDebugEvent()));
142 connect(this, SIGNAL(modulesLoaded()), this, SLOT(slotModulesLoaded()));
145 bool CdbEnginePrivate::init(QString *errorMessage)
147 enum { bufLen = 10240 };
149 if (!CdbCore::CoreEngine::init(m_options->path, errorMessage))
151 CdbDebugOutput *output = new CdbDebugOutput;
152 connect(output, SIGNAL(showMessage(QString,int,int)),
153 m_engine, SLOT(showMessage(QString,int,int)),
154 Qt::QueuedConnection); // Multithread invocation when loading dumpers.
155 setDebugOutput(DebugOutputBasePtr(output));
156 setDebugEventCallback(DebugEventCallbackBasePtr(new CdbDebugEventCallback(m_engine)));
162 DebuggerEngine *CdbEngine::create(const DebuggerStartParameters &sp,
163 QString *errorMessage)
165 CdbEngine *rc = new CdbEngine(sp);
166 if (rc->m_d->init(errorMessage)) {
167 rc->syncDebuggerPaths();
174 void CdbEnginePrivate::updateCodeLevel()
176 const CdbCore::CoreEngine::CodeLevel cl = theDebuggerBoolSetting(OperateByInstruction) ?
177 CdbCore::CoreEngine::CodeLevelAssembly : CdbCore::CoreEngine::CodeLevelSource;
181 CdbEnginePrivate::~CdbEnginePrivate()
186 void CdbEnginePrivate::clearForRun()
189 qDebug() << Q_FUNC_INFO;
191 m_breakEventMode = BreakEventHandle;
192 m_eventThreadId = m_interruptArticifialThreadId = -1;
193 m_interrupted = false;
195 m_stoppedReason = StoppedOther;
196 m_stoppedMessage.clear();
197 m_engine->threadsHandler()->notifyRunning();
200 void CdbEnginePrivate::cleanStackTrace()
202 if (m_currentStackTrace) {
203 delete m_currentStackTrace;
204 m_currentStackTrace = 0;
206 m_firstActivatedFrame = false;
207 m_editorToolTipCache.clear();
210 CdbEngine::CdbEngine(const DebuggerStartParameters &startParameters) :
211 DebuggerEngine(startParameters),
212 m_d(new CdbEnginePrivate(this))
214 m_d->m_consoleStubProc.setMode(Utils::ConsoleProcess::Suspend);
215 connect(&m_d->m_consoleStubProc, SIGNAL(processMessage(QString,bool)),
216 this, SLOT(slotConsoleStubMessage(QString, bool)));
217 connect(&m_d->m_consoleStubProc, SIGNAL(processStarted()),
218 this, SLOT(slotConsoleStubStarted()));
219 connect(&m_d->m_consoleStubProc, SIGNAL(wrapperStopped()),
220 this, SLOT(slotConsoleStubTerminated()));
223 CdbEngine::~CdbEngine()
228 void CdbEngine::setState(DebuggerState state, const char *func, int line)
231 qDebug() << "setState(" << state << ") at " << func << ':' << line;
232 DebuggerEngine::setState(state);
235 void CdbEngine::shutdownInferior()
237 notifyInferiorShutdownOk();
240 void CdbEngine::shutdownEngine()
245 QString CdbEngine::editorToolTip(const QString &exp, const QString &function)
247 // Figure the editor tooltip. Ask the frame context of the
248 // function if it is a local variable it knows. If that is not
249 // the case, try to evaluate via debugger
250 QString errorMessage;
252 // Find the frame of the function if there is any
253 CdbSymbolGroupContext *frame = 0;
254 if (m_d->m_currentStackTrace && !function.isEmpty()) {
255 const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
257 qDebug() << "CdbEngine::editorToolTip" << exp << function << frameIndex;
258 if (frameIndex != -1)
259 frame = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
261 if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
263 // No function/symbol context found, try to evaluate in current context.
264 // Do not append type as this will mostly be 'long long' for integers, etc.
267 qDebug() << "Defaulting to expression";
268 if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage))
273 void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
275 typedef CdbEnginePrivate::EditorToolTipCache EditorToolTipCache;
277 qDebug() << Q_FUNC_INFO << '\n' << cursorPos;
278 // Need a stopped debuggee and a cpp file
279 if (!m_d->m_hDebuggeeProcess || m_d->isDebuggeeRunning())
281 if (!isCppEditor(editor))
283 // Determine expression and function
289 const QString exp = cppExpressionAt(editor, cursorPos, &line, &column, &function);
290 if (function.isEmpty() || exp.isEmpty())
292 // Check cache (key containing function) or try to figure out expression
293 QString cacheKey = function;
294 cacheKey += QLatin1Char('@');
296 const EditorToolTipCache::const_iterator cit = m_d->m_editorToolTipCache.constFind(cacheKey);
297 if (cit != m_d->m_editorToolTipCache.constEnd()) {
298 toolTip = cit.value();
300 toolTip = editorToolTip(exp, function);
301 if (!toolTip.isEmpty())
302 m_d->m_editorToolTipCache.insert(cacheKey, toolTip);
306 QToolTip::hideText();
307 if (!toolTip.isEmpty())
308 QToolTip::showText(mousePos, toolTip);
311 void CdbEnginePrivate::clearDisplay()
313 m_engine->threadsHandler()->removeAll();
314 m_engine->modulesHandler()->removeAll();
315 m_engine->registerHandler()->removeAll();
318 void CdbEnginePrivate::checkVersion()
320 static bool versionNotChecked = true;
321 // Check for version 6.11 (extended expression syntax)
322 if (versionNotChecked) {
323 versionNotChecked = false;
324 // Get engine DLL version
325 QString errorMessage;
326 const QString version = Utils::winGetDLLVersion(Utils::WinDLLProductVersion, dbengDLL(), &errorMessage);
327 if (version.isEmpty()) {
328 qWarning("%s\n", qPrintable(errorMessage));
332 const double minVersion = 6.11;
333 m_engine->showMessage(CdbEngine::tr("Version: %1").arg(version));
334 if (version.toDouble() < minVersion) {
335 const QString msg = CdbEngine::tr(
336 "<html>The installed version of the <i>Debugging Tools for Windows</i> (%1) "
337 "is rather old. Upgrading to version %2 is recommended "
338 "for the proper display of Qt's data types.</html>").arg(version).arg(minVersion);
339 Core::ICore::instance()->showWarningWithOptions(CdbEngine::tr("Debugger"), msg, QString(),
340 QLatin1String(Constants::DEBUGGER_SETTINGS_CATEGORY),
341 CdbOptionsPage::settingsId());
346 void CdbEngine::startupChecks()
348 // Check symbol server unless the user has an external/internal setup
349 if (!qgetenv("_NT_SYMBOL_PATH").isEmpty()
350 || CdbOptions::indexOfSymbolServerPath(m_d->m_options->symbolPaths) != -1)
352 // Prompt to use Symbol server unless the user checked "No nagging".
353 Core::ICore *core = Core::ICore::instance();
354 const QString nagSymbolServerKey = CdbOptions::settingsGroup() + QLatin1String("/NoPromptSymbolServer");
355 bool noFurtherNagging = core->settings()->value(nagSymbolServerKey, false).toBool();
356 if (noFurtherNagging)
359 const QString symServUrl = QLatin1String("http://support.microsoft.com/kb/311503");
360 const QString msg = tr("<html><head/><body><p>The debugger is not configured to use the public "
361 "<a href=\"%1\">Microsoft Symbol Server</a>. This is recommended "
362 "for retrieval of the symbols of the operating system libraries.</p>"
363 "<p><i>Note:</i> A fast internet connection is required for this to work smoothly. Also, a delay "
364 "might occur when connecting for the first time.</p>"
365 "<p>Would you like to set it up?</p></br>"
366 "</body></html>").arg(symServUrl);
367 const QDialogButtonBox::StandardButton answer =
368 Utils::CheckableMessageBox::question(core->mainWindow(), tr("Symbol Server"), msg,
369 tr("Do not ask again"), &noFurtherNagging);
370 core->settings()->setValue(nagSymbolServerKey, noFurtherNagging);
371 if (answer == QDialogButtonBox::No)
373 // Prompt for path and add it. Synchronize QSetting and debugger.
374 const QString cacheDir = CdbSymbolPathListEditor::promptCacheDirectory(core->mainWindow());
375 if (cacheDir.isEmpty())
377 m_d->m_options->symbolPaths.push_back(CdbOptions::symbolServerPath(cacheDir));
378 m_d->m_options->toSettings(core->settings());
382 void CdbEngine::setupEngine()
384 QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
385 const DebuggerStartParameters &sp = startParameters();
386 if (debugCDBExecution)
387 qDebug() << "startDebugger";
388 CdbCore::BreakPoint::clearNormalizeFileNameCache();
391 if (m_d->m_hDebuggeeProcess) {
392 warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged."));
393 notifyEngineSetupFailed();
396 switch (sp.startMode) {
399 warning(QLatin1String("Internal error: Mode not supported."));
400 notifyEngineSetupFailed();
405 m_d->m_mode = sp.startMode;
407 m_d->m_inferiorStartupComplete = false;
409 QString errorMessage;
410 if (!m_d->setBreakOnThrow(theDebuggerBoolSetting(BreakOnThrow), &errorMessage))
411 showMessage(errorMessage, LogWarning);
412 m_d->setVerboseSymbolLoading(m_d->m_options->verboseSymbolLoading);
413 // Figure out dumper. @TODO: same in gdb...
414 const QString dumperLibName = QDir::toNativeSeparators(qtDumperLibraryName());
415 bool dumperEnabled = m_d->m_mode != AttachCore
416 && m_d->m_mode != AttachCrashedExternal
417 && qtDumperLibraryEnabled();
419 const QFileInfo fi(dumperLibName);
421 const QStringList &locations = qtDumperLibraryLocations();
422 const QString loc = locations.join(QLatin1String(", "));
423 const QString msg = tr("The dumper library was not found at %1.").arg(loc);
424 showQtDumperLibraryWarning(msg);
425 dumperEnabled = false;
428 m_d->m_dumper->reset(dumperLibName, dumperEnabled);
429 notifyEngineSetupOk();
432 void CdbEngine::setupInferior()
434 QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
435 notifyInferiorSetupOk();
438 void CdbEngine::runEngine()
440 QTC_ASSERT(state() == InferiorRunRequested, qDebug() << state());
441 showStatusMessage("Starting Debugger", messageTimeOut);
443 const DebuggerStartParameters &sp = startParameters();
445 bool needWatchTimer = false;
446 QString errorMessage;
448 m_d->updateCodeLevel();
449 m_d->m_ignoreInitialBreakPoint = false;
450 switch (m_d->m_mode) {
452 case AttachCrashedExternal:
453 rc = startAttachDebugger(sp.attachPID, m_d->m_mode, &errorMessage);
454 needWatchTimer = true; // Fetch away module load, etc. even if crashed
458 if (sp.useTerminal) {
459 // Attaching to console processes triggers an initial breakpoint, which we do not want
460 m_d->m_ignoreInitialBreakPoint = true;
461 // Launch console stub and wait for its startup
462 m_d->m_consoleStubProc.stop(); // We leave the console open, so recycle it now.
463 m_d->m_consoleStubProc.setWorkingDirectory(sp.workingDirectory);
464 m_d->m_consoleStubProc.setEnvironment(sp.environment);
465 rc = m_d->m_consoleStubProc.start(sp.executable, sp.processArgs);
467 errorMessage = tr("The console stub process was unable to start '%1'.").arg(sp.executable);
468 // continues in slotConsoleStubStarted()...
470 needWatchTimer = true;
471 rc = m_d->startDebuggerWithExecutable(sp.workingDirectory,
479 errorMessage = tr("Attaching to core files is not supported!");
484 m_d->startWatchTimer();
485 notifyInferiorSetupOk();
487 warning(errorMessage);
488 notifyInferiorSetupFailed();
492 bool CdbEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage)
494 // Need to attach invasively, otherwise, no notification signals
495 // for for CreateProcess/ExitProcess occur.
496 // Initial breakpoint occur:
497 // 1) Desired: When attaching to a crashed process
498 // 2) Undesired: When starting up a console process, in conjunction
499 // with the 32bit Wow-engine
500 // As of version 6.11, the flag only affects 1). 2) Still needs to be suppressed
501 // by lookup at the state of the application (startup trap). However,
502 // there is no startup trap when attaching to a process that has been
503 // running for a while. (see notifyException).
504 const bool suppressInitialBreakPoint = sm != AttachCrashedExternal;
505 return m_d->startAttachDebugger(pid, suppressInitialBreakPoint, errorMessage);
508 void CdbEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle)
510 m_engine->notifyInferiorRunRequested();
511 setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle));
512 ULONG currentThreadId;
513 if (SUCCEEDED(interfaces().debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, ¤tThreadId))) {
514 m_currentThreadId = currentThreadId;
516 m_currentThreadId = 0;
518 // Clear any saved breakpoints and set initial breakpoints
519 m_engine->executeDebuggerCommand(QLatin1String("bc"));
520 if (m_engine->breakHandler()->hasPendingBreakpoints()) {
521 if (debugCDBExecution)
522 qDebug() << "processCreatedAttached: Syncing breakpoints";
523 m_engine->attemptBreakpointSynchronization();
525 // Attaching to crashed: This handshake (signalling an event) is required for
526 // the exception to be delivered to the debugger
527 // Also, see special handling in slotModulesLoaded().
528 if (m_mode == AttachCrashedExternal) {
529 const QString crashParameter = m_engine->startParameters().crashParameter;
530 if (!crashParameter.isEmpty()) {
531 ULONG64 evtNr = crashParameter.toULongLong();
532 const HRESULT hr = interfaces().debugControl->SetNotifyEventHandle(evtNr);
534 m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(CdbCore::msgComFailed("SetNotifyEventHandle", hr)));
537 m_engine->notifyInferiorRunOk();
538 if (debugCDBExecution)
539 qDebug() << "<processCreatedAttached";
542 void CdbEngine::processTerminated(unsigned long exitCode)
544 showMessage(tr("The process exited with exit code %1.").arg(exitCode));
545 //if (state() != InferiorStopRequested)
546 // setState(InferiorStopRequested, Q_FUNC_INFO, __LINE__);
547 //setState(InferiorStopOk, Q_FUNC_INFO, __LINE__);
548 //setState(InferiorShutdownRequested, Q_FUNC_INFO, __LINE__);
549 m_d->setDebuggeeHandles(0, 0);
551 //setState(InferiorShutdownOk, Q_FUNC_INFO, __LINE__);
552 // Avoid calls from event handler.
553 //QTimer::singleShot(0, this, SLOT(quitDebugger()));
554 notifyInferiorExited(); // FIXME AAA: correnct?
557 bool CdbEnginePrivate::endInferior(EndInferiorAction action, QString *errorMessage)
559 // Process must be stopped in order to terminate
560 //m_engine->setState(InferiorShutdownRequested, Q_FUNC_INFO, __LINE__); // pretend it is shutdown
561 const bool wasRunning = isDebuggeeRunning();
563 interruptInterferiorProcess(errorMessage);
564 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
566 bool success = false;
569 if (detachCurrentProcess(errorMessage))
572 case TerminateInferior:
574 // The exit process event handler will not be called.
575 terminateCurrentProcess(errorMessage);
580 if (terminateProcesses(errorMessage))
583 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
586 // Perform cleanup even when failed..no point clinging to the process
587 setDebuggeeHandles(0, 0);
590 m_engine->notifyInferiorShutdownOk();
592 m_engine->notifyInferiorShutdownFailed();
596 // End debugging. Note that this can invoked via user action
597 // or the processTerminated() event handler, in which case it
598 // must not kill the process again.
599 void CdbEnginePrivate::endDebugging(EndDebuggingMode em)
602 qDebug() << Q_FUNC_INFO << em;
604 const DebuggerState oldState = m_engine->state();
605 if (oldState == DebuggerNotReady || m_mode == AttachCore)
607 // Do we need to stop the process?
608 QString errorMessage;
609 if (oldState != InferiorShutdownOk && m_hDebuggeeProcess) {
610 EndInferiorAction action;
612 case EndDebuggingAuto:
613 action = (m_mode == AttachExternal || m_mode == AttachCrashedExternal) ?
614 DetachInferior : TerminateInferior;
616 case EndDebuggingDetach:
617 action = DetachInferior;
619 case EndDebuggingTerminate:
620 action = TerminateInferior;
624 qDebug() << Q_FUNC_INFO << action;
625 // Need a stopped debuggee to act
626 if (!endInferior(action, &errorMessage)) {
627 errorMessage = QString::fromLatin1("Unable to detach from/end the debuggee: %1").arg(errorMessage);
628 m_engine->showMessage(errorMessage, LogError);
630 errorMessage.clear();
632 // Clean up resources (open files, etc.)
633 //m_engine->setState(EngineShutdownRequested, Q_FUNC_INFO, __LINE__);
635 const bool endedCleanly = endSession(&errorMessage);
636 //m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__);
638 errorMessage = QString::fromLatin1("There were errors trying to end debugging:\n%1").arg(errorMessage);
639 m_engine->showMessage(errorMessage, LogError);
643 void CdbEngine::detachDebugger()
645 m_d->endDebugging(CdbEnginePrivate::EndDebuggingDetach);
648 CdbSymbolGroupContext *CdbEnginePrivate::getSymbolGroupContext(int frameIndex, QString *errorMessage) const
650 if (!m_currentStackTrace) {
651 *errorMessage = QLatin1String(msgNoStackTraceC);
654 if (CdbSymbolGroupContext *sg = m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, errorMessage))
659 void CdbEngine::evaluateWatcher(WatchData *wd)
661 if (debugCDBWatchHandling)
662 qDebug() << Q_FUNC_INFO << wd->exp;
663 QString errorMessage;
666 QString exp = wd->exp;
667 // Remove locals watch prefix.
668 if (exp.startsWith(QLatin1String("local.")))
670 if (m_d->evaluateExpression(exp, &value, &type, &errorMessage)) {
674 wd->setValue(errorMessage);
675 wd->setTypeUnneeded();
677 wd->setHasChildren(false);
680 void CdbEngine::updateWatchData(const WatchData &incomplete)
682 // Watch item was edited while running
683 if (m_d->isDebuggeeRunning())
686 if (debugCDBWatchHandling)
687 qDebug() << Q_FUNC_INFO << "\n " << incomplete.toString();
689 if (incomplete.iname.startsWith("watch.")) {
690 WatchData watchData = incomplete;
691 evaluateWatcher(&watchData);
692 watchHandler()->insertData(watchData);
696 const int frameIndex = stackHandler()->currentIndex();
698 bool success = false;
699 QString errorMessage;
701 CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
704 if (!sg->completeData(incomplete, watchHandler(), &errorMessage))
709 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
710 if (debugCDBWatchHandling > 1)
711 qDebug() << *watchHandler()->model(LocalsWatch);
714 // Continue inferior with a debugger command, such as "p", "pt"
715 // or its thread variations
716 bool CdbEnginePrivate::executeContinueCommand(const QString &command)
719 qDebug() << Q_FUNC_INFO << command;
721 updateCodeLevel(); // Step by instruction
722 m_engine->notifyInferiorRunRequested();
723 m_engine->showMessage(CdbEngine::tr("Continuing with '%1'...").arg(command));
724 QString errorMessage;
725 const bool success = executeDebuggerCommand(command, &errorMessage);
727 m_engine->notifyInferiorRunOk();
730 m_engine->notifyInferiorRunFailed();
731 m_engine->warning(CdbEngine::tr("Unable to continue: %1").arg(errorMessage));
736 static inline QString msgStepFailed(unsigned long executionStatus, int threadId, const QString &why)
738 if (executionStatus == DEBUG_STATUS_STEP_OVER)
739 return QString::fromLatin1("Thread %1: Unable to step over: %2").arg(threadId).arg(why);
740 return QString::fromLatin1("Thread %1: Unable to step into: %2").arg(threadId).arg(why);
743 // Step out has to be done via executing 'gu'. TODO: Remove once it is
744 // accessible via normal API for SetExecutionStatus().
746 enum { CdbExtendedExecutionStatusStepOut = 7452347 };
748 // Step with DEBUG_STATUS_STEP_OVER ('p'-command),
749 // DEBUG_STATUS_STEP_INTO ('t'-trace-command) or
750 // CdbExtendedExecutionStatusStepOut ("gu"-command)
751 // its reverse equivalents in the case of single threads.
753 bool CdbEngine::step(unsigned long executionStatus)
755 if (debugCDBExecution)
756 qDebug() << ">step" << executionStatus << "curr " << m_d->m_currentThreadId << " evt " << m_d->m_eventThreadId;
758 // State of reverse stepping as of 10/2009 (Debugging tools 6.11@404):
759 // The constants exist, but invoking the calls leads to E_NOINTERFACE.
760 // Also there is no CDB command for it.
761 if (executionStatus == DEBUG_STATUS_REVERSE_STEP_OVER || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO) {
762 warning(tr("Reverse stepping is not implemented."));
766 // Do not step the artifical thread created to interrupt the debuggee.
767 if (m_d->m_interrupted && m_d->m_currentThreadId == m_d->m_interruptArticifialThreadId) {
768 warning(tr("Thread %1 cannot be stepped.").arg(m_d->m_currentThreadId));
772 // SetExecutionStatus() continues the thread that triggered the
773 // stop event (~# p). This can be confusing if the user is looking
774 // at the stack trace of another thread and wants to step that one. If that
775 // is the case, explicitly tell it to step the current thread using a command.
776 const int triggeringEventThread = m_d->m_eventThreadId;
777 const bool sameThread = triggeringEventThread == -1
778 || m_d->m_currentThreadId == triggeringEventThread
779 || threadsHandler()->threads().size() == 1;
780 m_d->clearForRun(); // clears thread ids
781 m_d->updateCodeLevel(); // Step by instruction or source line
782 notifyInferiorRunRequested();
783 bool success = false;
784 if (sameThread && executionStatus != CdbExtendedExecutionStatusStepOut) { // Step event-triggering thread, use fast API
785 const HRESULT hr = m_d->interfaces().debugControl->SetExecutionStatus(executionStatus);
786 success = SUCCEEDED(hr);
788 warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, CdbCore::msgComFailed("SetExecutionStatus", hr)));
790 // Need to use a command to explicitly specify the current thread
792 QTextStream str(&command);
793 str << '~' << m_d->m_currentThreadId << ' ';
794 switch (executionStatus) {
795 case DEBUG_STATUS_STEP_OVER:
798 case DEBUG_STATUS_STEP_INTO:
801 case CdbExtendedExecutionStatusStepOut:
805 showMessage(tr("Stepping %1").arg(command));
806 const HRESULT hr = m_d->interfaces().debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, command.toLatin1().constData(), DEBUG_EXECUTE_ECHO);
807 success = SUCCEEDED(hr);
809 warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, msgDebuggerCommandFailed(command, hr)));
812 // Oddity: Step into will first break at the calling function. Ignore
813 if (executionStatus == DEBUG_STATUS_STEP_INTO || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO)
814 m_d->m_breakEventMode = CdbEnginePrivate::BreakEventIgnoreOnce;
815 m_d->startWatchTimer();
816 notifyInferiorRunOk();
818 notifyInferiorRunFailed();
820 if (debugCDBExecution)
821 qDebug() << "<step samethread" << sameThread << "succeeded" << success;
825 void CdbEngine::executeStep()
827 step(isReverseDebugging() ? DEBUG_STATUS_REVERSE_STEP_INTO : DEBUG_STATUS_STEP_INTO);
830 void CdbEngine::executeNext()
832 step(isReverseDebugging() ? DEBUG_STATUS_REVERSE_STEP_OVER : DEBUG_STATUS_STEP_OVER);
835 void CdbEngine::executeStepI()
837 executeStep(); // Step into by instruction (figured out by step)
840 void CdbEngine::executeNextI()
842 executeNext(); // Step over by instruction (figured out by step)
845 void CdbEngine::executeStepOut()
847 if (!isReverseDebugging())
848 step(CdbExtendedExecutionStatusStepOut);
851 void CdbEngine::continueInferior()
853 QString errorMessage;
854 if (!m_d->continueInferior(&errorMessage))
855 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
858 // Continue process without notifications
859 bool CdbEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* = 0 */)
861 if (debugCDBExecution)
862 qDebug() << "continueInferiorProcess";
863 const HRESULT hr = interfaces().debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
865 const QString errorMessage = CdbCore::msgComFailed("SetExecutionStatus", hr);
866 if (errorMessagePtr) {
867 *errorMessagePtr = errorMessage;
869 m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
876 // Continue process with notifications
877 bool CdbEnginePrivate::continueInferior(QString *errorMessage)
879 // Check state: Are we running?
880 const ULONG ex = executionStatus();
882 qDebug() << Q_FUNC_INFO << "\n ex=" << ex;
884 if (ex == DEBUG_STATUS_GO) {
885 m_engine->warning(QLatin1String("continueInferior() called while debuggee is running."));
889 m_engine->notifyInferiorRunRequested();
890 bool success = false;
895 m_engine->resetLocation();
896 m_engine->showStatusMessage(CdbEngine::tr("Running requested..."), messageTimeOut);
898 if (!continueInferiorProcess(errorMessage))
905 m_engine->notifyInferiorRunOk();
907 m_engine->notifyInferiorRunFailed();
912 bool CdbEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
915 // Interrupt the interferior process without notifications
916 // Could use setInterrupt, but that does not work.
917 if (debugCDBExecution) {
918 qDebug() << "interruptInterferiorProcess ex=" << executionStatus();
920 const bool rc = debugBreakProcess(m_hDebuggeeProcess, errorMessage);
922 m_interrupted = true;
926 void CdbEnginePrivate::slotModulesLoaded()
928 // Attaching to crashed windows processes: Unless QtCreator is
929 // spawned by the debug handler and inherits the handles,
930 // the event handling does not work reliably (that is, the crash
931 // event is not delivered). In that case, force a break
932 if (m_mode == AttachCrashedExternal && m_engine->state() != InferiorStopOk)
933 QTimer::singleShot(10, m_engine, SLOT(slotBreakAttachToCrashed()));
936 void CdbEngine::slotBreakAttachToCrashed()
938 // Force a break when attaching to crashed process (if Creator was not spawned
940 if (state() != InferiorStopOk) {
941 showMessage(QLatin1String("Forcing break..."));
942 m_d->m_dumper->disable();
947 void CdbEngine::interruptInferior()
949 QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state());
950 if (!m_d->m_hDebuggeeProcess || !m_d->isDebuggeeRunning())
953 QString errorMessage;
954 if (!m_d->interruptInterferiorProcess(&errorMessage)) {
955 notifyInferiorStopFailed();
956 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
960 void CdbEngine::executeRunToLine(const QString &fileName, int lineNumber)
962 showMessage(tr("Running up to %1:%2...").arg(fileName).arg(lineNumber));
963 QString errorMessage;
964 CdbCore::BreakPoint tempBreakPoint;
965 tempBreakPoint.fileName = fileName;
966 tempBreakPoint.lineNumber = lineNumber;
967 tempBreakPoint.oneShot = true;
968 const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage)
969 && m_d->continueInferior(&errorMessage);
971 warning(errorMessage);
974 void CdbEngine::executeRunToFunction(const QString &functionName)
976 showMessage(tr("Running up to function '%1()'...").arg(functionName));
977 QString errorMessage;
978 CdbCore::BreakPoint tempBreakPoint;
979 tempBreakPoint.funcName = functionName;
980 tempBreakPoint.oneShot = true;
981 const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage)
982 && m_d->continueInferior(&errorMessage);
984 warning(errorMessage);
987 void CdbEngine::executeJumpToLine(const QString & /* fileName */, int /*lineNumber*/)
989 warning(tr("Jump to line is not implemented"));
992 void CdbEngine::assignValueInDebugger(const QString &expr, const QString &value)
995 qDebug() << Q_FUNC_INFO << expr << value;
996 const int frameIndex = stackHandler()->currentIndex();
997 QString errorMessage;
998 bool success = false;
1001 CdbSymbolGroupContext *sg = m_d->getSymbolGroupContext(frameIndex, &errorMessage);
1004 if (!sg->assignValue(expr, value, &newValue, &errorMessage))
1007 if (WatchData *fwd = watchHandler()->findItem(expr.toLatin1())) {
1008 fwd->setValue(newValue);
1009 watchHandler()->insertData(*fwd);
1010 watchHandler()->updateWatchers();
1015 const QString msg = tr("Unable to assign the value '%1' to '%2': %3").arg(value, expr, errorMessage);
1020 void CdbEngine::executeDebuggerCommand(const QString &command)
1022 QString errorMessage;
1023 if (!m_d->executeDebuggerCommand(command, &errorMessage))
1024 warning(errorMessage);
1027 void CdbEngine::activateFrame(int frameIndex)
1030 qDebug() << Q_FUNC_INFO << frameIndex;
1032 if (state() != InferiorStopOk) {
1033 qWarning("WARNING %s: invoked while debuggee is running\n", Q_FUNC_INFO);
1037 QString errorMessage;
1038 bool success = false;
1040 const int oldIndex = stackHandler()->currentIndex();
1041 if (frameIndex >= stackHandler()->stackSize()) {
1042 errorMessage = msgStackIndexOutOfRange(frameIndex, stackHandler()->stackSize());
1046 if (oldIndex != frameIndex)
1047 stackHandler()->setCurrentIndex(frameIndex);
1049 const StackFrame &frame = stackHandler()->currentFrame();
1051 const bool showAssembler = !frame.isUsable();
1052 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1053 watchHandler()->beginCycle();
1054 watchHandler()->endCycle();
1055 QAction *assemblerAction = theDebuggerAction(OperateByInstruction);
1056 if (!assemblerAction->isChecked())
1057 assemblerAction->trigger();
1062 gotoLocation(frame, true);
1064 if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
1065 watchHandler()->beginCycle();
1066 if (CdbSymbolGroupContext *sgc = m_d->getSymbolGroupContext(frameIndex, &errorMessage))
1067 success = sgc->populateModelInitially(watchHandler(), &errorMessage);
1068 watchHandler()->endCycle();
1075 const QString msg = QString::fromLatin1("Internal error: activateFrame() failed for frame #%1 of %2, thread %3: %4").
1076 arg(frameIndex).arg(stackHandler()->stackSize()).
1077 arg(m_d->m_currentThreadId).arg(errorMessage);
1080 m_d->m_firstActivatedFrame = false;
1083 void CdbEngine::selectThread(int index)
1086 qDebug() << Q_FUNC_INFO << index;
1088 // Reset location arrow.
1091 threadsHandler()->setCurrentThread(index);
1092 const int newThreadId = threadsHandler()->threads().at(index).id;
1093 if (newThreadId != m_d->m_currentThreadId) {
1094 m_d->m_currentThreadId = threadsHandler()->threads().at(index).id;
1095 m_d->updateStackTrace();
1099 void CdbEngine::attemptBreakpointSynchronization()
1101 if (!m_d->m_hDebuggeeProcess) // Sometimes called from the breakpoint Window
1103 QString errorMessage;
1104 if (!m_d->attemptBreakpointSynchronization(&errorMessage))
1105 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
1108 bool CdbEnginePrivate::attemptBreakpointSynchronization(QString *errorMessage)
1110 if (!m_hDebuggeeProcess) {
1111 *errorMessage = QLatin1String("attemptBreakpointSynchronization() called while debugger is not running");
1114 // This is called from
1115 // 1) CreateProcessEvent with the halted engine
1116 // 2) from the break handler, potentially while the debuggee is running
1117 // If the debuggee is running (for which the execution status is
1118 // no reliable indicator), we temporarily halt and have ourselves
1119 // called again from the debug event handler.
1122 const bool wasRunning = !CdbCore::BreakPoint::getBreakPointCount(interfaces().debugControl, &dummy);
1124 qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning;
1127 const HandleBreakEventMode oldMode = m_breakEventMode;
1128 m_breakEventMode = BreakEventSyncBreakPoints;
1129 if (!interruptInterferiorProcess(errorMessage)) {
1130 m_breakEventMode = oldMode;
1136 QStringList warnings;
1137 const bool ok = synchronizeBreakPoints(interfaces().debugControl,
1138 interfaces().debugSymbols,
1139 m_engine->breakHandler(),
1140 errorMessage, &warnings);
1141 if (const int warningsCount = warnings.size())
1142 for (int w = 0; w < warningsCount; w++)
1143 m_engine->warning(warnings.at(w));
1147 void CdbEngine::fetchDisassembler(DisassemblerViewAgent *agent)
1149 StackFrame frame = agent->frame();
1150 enum { ContextLines = 40 };
1152 QString errorMessage;
1156 if (!frame.file.isEmpty())
1157 address = frame.address;
1158 if (address.isEmpty())
1159 address = agent->address();
1161 qDebug() << "fetchDisassembler" << address << " Agent: " << agent->address()
1162 << " Frame" << frame.file << frame.line << frame.address;
1163 if (address.isEmpty()) { // Clear window
1164 agent->setContents(QString());
1168 if (address.startsWith(QLatin1String("0x")))
1169 address.remove(0, 2);
1170 const int addressFieldWith = address.size(); // For the Marker
1172 const ULONG64 offset = address.toULongLong(&ok, 16);
1174 errorMessage = QString::fromLatin1("Internal error: Invalid address for disassembly: '%1'.").arg(agent->address());
1177 QString disassembly;
1178 QApplication::setOverrideCursor(Qt::WaitCursor);
1179 ok = dissassemble(m_d, offset, ContextLines, ContextLines, addressFieldWith, QTextStream(&disassembly), &errorMessage);
1180 QApplication::restoreOverrideCursor();
1183 agent->setContents(disassembly);
1188 agent->setContents(QString());
1189 warning(errorMessage);
1193 void CdbEngine::fetchMemory(MemoryViewAgent *agent, QObject *token, quint64 addr, quint64 length)
1195 if (!m_d->m_hDebuggeeProcess && !length)
1198 QByteArray data(length, '\0');
1199 const HRESULT hr = m_d->interfaces().debugDataSpaces->ReadVirtual(addr, data.data(), length, &received);
1201 warning(tr("Unable to retrieve %1 bytes of memory at 0x%2: %3").
1202 arg(length).arg(addr, 0, 16).arg(CdbCore::msgComFailed("ReadVirtual", hr)));
1205 if (received < length)
1206 data.truncate(received);
1207 agent->addLazyData(token, addr, data);
1210 void CdbEngine::reloadModules()
1214 void CdbEngine::loadSymbols(const QString &moduleName)
1217 qDebug() << Q_FUNC_INFO << moduleName;
1220 void CdbEngine::loadAllSymbols()
1223 qDebug() << Q_FUNC_INFO;
1226 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1229 QString errorMessage;
1230 bool success = false;
1232 if (m_d->isDebuggeeRunning()) {
1233 errorMessage = tr("Cannot retrieve symbols while the debuggee is running.");
1236 if (!getModuleSymbols(m_d->interfaces().debugSymbols, moduleName, &rc, &errorMessage))
1241 warning(errorMessage);
1242 showModuleSymbols(moduleName, rc);
1245 void CdbEngine::reloadRegisters()
1247 if (state() != InferiorStopOk)
1249 const int intBase = 10;
1251 qDebug() << Q_FUNC_INFO << intBase;
1253 QString errorMessage;
1254 const Registers registers = getRegisters(m_d->interfaces().debugControl, m_d->interfaces().debugRegisters, &errorMessage, intBase);
1255 if (registers.isEmpty() && !errorMessage.isEmpty())
1256 warning(msgFunctionFailed("reloadRegisters" , errorMessage));
1257 registerHandler()->setRegisters(registers);
1260 void CdbEngine::slotConsoleStubStarted()
1262 const qint64 appPid = m_d->m_consoleStubProc.applicationPID();
1264 qDebug() << Q_FUNC_INFO << appPid;
1265 // Attach to console process.
1266 QString errorMessage;
1267 if (startAttachDebugger(appPid, AttachExternal, &errorMessage)) {
1268 m_d->startWatchTimer();
1269 notifyInferiorPid(appPid);
1271 QMessageBox::critical(DebuggerUISwitcher::instance()->mainWindow(), tr("Debugger Error"), errorMessage);
1275 void CdbEngine::slotConsoleStubMessage(const QString &msg, bool)
1277 QMessageBox::critical(DebuggerUISwitcher::instance()->mainWindow(), tr("Debugger Error"), msg);
1280 void CdbEngine::slotConsoleStubTerminated()
1285 void CdbEngine::warning(const QString &msg)
1287 showMessage(msg, LogWarning);
1288 qWarning("%s\n", qPrintable(msg));
1291 void CdbEnginePrivate::notifyException(long code, bool fatal, const QString &message)
1293 if (debugCDBExecution)
1294 qDebug() << "notifyException code" << code << " fatal=" << fatal;
1295 // Suppress the initial breakpoint that occurs when
1296 // attaching to a console (If a breakpoint is encountered before startup
1297 // is complete, see startAttachDebugger()).
1299 case winExceptionStartupCompleteTrap:
1300 m_inferiorStartupComplete = true;
1302 case EXCEPTION_BREAKPOINT:
1303 if (m_ignoreInitialBreakPoint && !m_inferiorStartupComplete && m_breakEventMode == BreakEventHandle) {
1304 m_engine->showMessage(CdbEngine::tr("Ignoring initial breakpoint..."));
1305 m_breakEventMode = BreakEventIgnoreOnce;
1309 // Cannot go over crash point to execute calls.
1311 m_dumper->disable();
1312 m_stoppedReason = StoppedCrash;
1313 m_stoppedMessage = message;
1317 static int threadIndexById(const ThreadsHandler *threadsHandler, int id)
1319 const Threads threads = threadsHandler->threads();
1320 const int count = threads.count();
1321 for (int i = 0; i < count; i++)
1322 if (threads.at(i).id == id)
1327 void CdbEnginePrivate::handleDebugEvent()
1329 if (debugCDBExecution)
1330 qDebug() << "handleDebugEvent mode " << m_breakEventMode
1331 << CdbCore::msgExecutionStatusString(executionStatus()) << " interrupt" << m_interrupted
1332 << " startupcomplete" << m_inferiorStartupComplete;
1333 // restore mode and do special handling
1334 const HandleBreakEventMode mode = m_breakEventMode;
1335 m_breakEventMode = BreakEventHandle;
1338 case BreakEventHandle: {
1339 // If this is triggered by breakpoint/crash: Set state to stopping
1340 // to avoid warnings as opposed to interrupt inferior
1341 //if (m_engine->state() != InferiorStopRequested) FIXME: AAA
1342 // m_engine->setState(InferiorStopRequested, Q_FUNC_INFO, __LINE__);
1343 m_engine->notifyInferiorStopOk();
1344 // Indicate artifical thread that is created when interrupting as such,
1345 // else use stop message with cleaned newlines and blanks.
1346 const QString currentThreadState =
1347 m_interrupted ? CdbEngine::tr("<interrupt thread>") :
1348 (m_stoppedReason == StoppedBreakpoint ? CdbEngine::tr("Breakpoint") :
1349 m_stoppedMessage.simplified() );
1350 m_eventThreadId = updateThreadList(currentThreadState);
1351 m_interruptArticifialThreadId = m_interrupted ? m_eventThreadId : -1;
1352 // Get thread to stop and its index. If avoidable, do not use
1353 // the artifical thread that is created when interrupting,
1354 // use the oldest thread 0 instead.
1355 ThreadsHandler *threadsHandler = m_engine->threadsHandler();
1356 m_currentThreadId = m_interrupted ? 0 : m_eventThreadId;
1357 int currentThreadIndex = -1;
1358 m_currentThreadId = -1;
1359 if (m_interrupted) {
1360 m_currentThreadId = 0;
1361 currentThreadIndex = threadIndexById(threadsHandler, m_currentThreadId);
1363 if (!m_interrupted || currentThreadIndex == -1) {
1364 m_currentThreadId = m_eventThreadId;
1365 currentThreadIndex = threadIndexById(threadsHandler, m_currentThreadId);
1367 const QString msg = m_interrupted ?
1368 CdbEngine::tr("Interrupted in thread %1, current thread: %2").arg(m_interruptArticifialThreadId).arg(m_currentThreadId) :
1369 CdbEngine::tr("Stopped, current thread: %1").arg(m_currentThreadId);
1370 m_engine->showMessage(msg);
1371 const int threadIndex = threadIndexById(threadsHandler, m_currentThreadId);
1372 if (threadIndex != -1)
1373 threadsHandler->setCurrentThread(threadIndex);
1377 case BreakEventIgnoreOnce:
1379 m_interrupted = false;
1381 case BreakEventSyncBreakPoints: {
1382 m_interrupted = false;
1383 // Temp stop to sync breakpoints
1384 QString errorMessage;
1385 attemptBreakpointSynchronization(&errorMessage);
1387 continueInferiorProcess(&errorMessage);
1388 if (!errorMessage.isEmpty())
1389 m_engine->warning(QString::fromLatin1("In handleDebugEvent: %1").arg(errorMessage));
1395 void CdbEnginePrivate::setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread)
1398 qDebug() << Q_FUNC_INFO << hDebuggeeProcess << hDebuggeeThread;
1399 m_hDebuggeeProcess = hDebuggeeProcess;
1400 m_hDebuggeeThread = hDebuggeeThread;
1403 // Set thread in CDB engine
1404 bool CdbEnginePrivate::setCDBThreadId(unsigned long threadId, QString *errorMessage)
1406 ULONG currentThreadId;
1407 HRESULT hr = interfaces().debugSystemObjects->GetCurrentThreadId(¤tThreadId);
1409 *errorMessage = CdbCore::msgComFailed("GetCurrentThreadId", hr);
1412 if (currentThreadId == threadId)
1414 hr = interfaces().debugSystemObjects->SetCurrentThreadId(threadId);
1416 *errorMessage = QString::fromLatin1("Failed to change to from thread %1 to %2: SetCurrentThreadId() failed: %3").
1417 arg(currentThreadId).arg(threadId).arg(CdbCore::msgDebugEngineComResult(hr));
1420 const QString msg = CdbEngine::tr("Changing threads: %1 -> %2").arg(currentThreadId).arg(threadId);
1421 m_engine->showStatusMessage(msg, 500);
1425 ULONG CdbEnginePrivate::updateThreadList(const QString ¤tThreadState)
1428 qDebug() << Q_FUNC_INFO << m_hDebuggeeProcess;
1431 ULONG currentThreadId;
1432 QString errorMessage;
1433 // When interrupting, an artifical thread with a breakpoint is created.
1434 const bool stopped = m_engine->state() == InferiorStopOk;
1435 if (!CdbStackTraceContext::getThreads(interfaces(),
1437 &threads, ¤tThreadId,
1439 m_engine->warning(errorMessage);
1440 // Indicate states 'stopped' or current thread state.
1441 // Do not indicate 'running' since we can't know if it is suspended.
1443 const QString state = CdbEngine::tr("stopped");
1444 const bool hasCurrentState = !currentThreadState.isEmpty();
1445 const int count = threads.size();
1446 for (int i= 0; i < count; i++) {
1447 threads[i].state = hasCurrentState && threads.at(i).id == currentThreadId ?
1448 currentThreadState : state;
1451 m_engine->threadsHandler()->setThreads(threads);
1452 return currentThreadId;
1455 // Figure out the thread to run the dumpers in (see notes on.
1456 // CdbDumperHelper). Avoid the artifical threads created by interrupt
1457 // and threads that are in waitFor().
1458 // A stricter version could only use the thread if it is the event
1459 // thread of a step or breakpoint hit (see CdbEnginePrivate::m_interrupted).
1461 static inline unsigned long dumperThreadId(const QList<StackFrame> &frames,
1462 unsigned long currentThread)
1465 return CdbDumperHelper::InvalidDumperCallThread;
1466 switch (CdbCore::StackTraceContext::specialFunction(frames.at(0).from, frames.at(0).function)) {
1467 case CdbCore::StackTraceContext::BreakPointFunction:
1468 case CdbCore::StackTraceContext::WaitFunction:
1469 return CdbDumperHelper::InvalidDumperCallThread;
1473 // Check remaining frames for wait
1474 const int waitCheckDepth = qMin(frames.size(), 5);
1475 for (int f = 1; f < waitCheckDepth; f++) {
1476 if (CdbCore::StackTraceContext::specialFunction(frames.at(f).from, frames.at(f).function)
1477 == CdbCore::StackTraceContext::WaitFunction)
1478 return CdbDumperHelper::InvalidDumperCallThread;
1480 return currentThread;
1483 // Format stop message with all available information.
1484 QString CdbEnginePrivate::stoppedMessage(const StackFrame *topFrame /* = 0 */) const
1488 if (topFrame->isUsable()) {
1489 // Stopped at basename:line
1490 const int lastSlashPos = topFrame->file.lastIndexOf(QLatin1Char('/'));
1491 const QString file = lastSlashPos == -1 ? topFrame->file : topFrame->file.mid(lastSlashPos + 1);
1492 msg = CdbEngine::tr("Stopped at %1:%2 in thread %3.").
1493 arg(file).arg(topFrame->line).arg(m_currentThreadId);
1495 // Somewhere in assembly code.
1496 if (topFrame->function.isEmpty()) {
1497 msg = CdbEngine::tr("Stopped at %1 in thread %2 (missing debug information).").
1498 arg(topFrame->address).arg(m_currentThreadId);
1500 msg = CdbEngine::tr("Stopped at %1 (%2) in thread %3 (missing debug information).").
1501 arg(topFrame->address).arg(topFrame->function).arg(m_currentThreadId);
1505 msg = CdbEngine::tr("Stopped in thread %1 (missing debug information).").arg(m_currentThreadId);
1508 if (!m_stoppedMessage.isEmpty()) {
1509 msg += QLatin1Char(' ');
1510 msg += m_stoppedMessage;
1515 void CdbEnginePrivate::updateStackTrace()
1518 qDebug() << Q_FUNC_INFO;
1519 // Create a new context
1521 QString errorMessage;
1522 m_engine->reloadRegisters();
1523 if (!setCDBThreadId(m_currentThreadId, &errorMessage)) {
1524 m_engine->warning(errorMessage);
1527 m_currentStackTrace =
1528 CdbStackTraceContext::create(m_dumper, &errorMessage);
1529 if (!m_currentStackTrace) {
1530 m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
1533 // Disassembling slows things down a bit. Assembler is still available via menu.
1535 m_engine->reloadDisassembler(); // requires stack trace
1537 const QList<StackFrame> stackFrames = m_currentStackTrace->stackFrames();
1538 // find the first usable frame and select it
1540 const int count = stackFrames.count();
1541 for (int i=0; i < count; ++i)
1542 if (stackFrames.at(i).isUsable()) {
1546 // Format stop message.
1547 const QString stopMessage = stoppedMessage(stackFrames.isEmpty() ? static_cast<const StackFrame *>(0) : &stackFrames.front());
1548 // Set up dumper with a thread (or invalid)
1549 const unsigned long dumperThread = dumperThreadId(stackFrames, m_currentThreadId);
1550 if (debugCDBExecution)
1551 qDebug() << "updateStackTrace() current: " << m_currentThreadId << " dumper=" << dumperThread;
1552 m_dumper->setDumperCallThread(dumperThread);
1554 m_engine->stackHandler()->setFrames(stackFrames);
1555 m_firstActivatedFrame = true;
1557 m_engine->stackHandler()->setCurrentIndex(current);
1558 m_engine->activateFrame(current);
1560 // Clean out variables
1561 m_engine->watchHandler()->beginCycle();
1562 m_engine->watchHandler()->endCycle();
1564 m_engine->watchHandler()->updateWatchers();
1565 // Show message after a lengthy dumper initialization
1566 m_engine->showMessage(stopMessage, StatusBar, 15000);
1569 void CdbEnginePrivate::updateModules()
1571 QList<Module> modules;
1572 QString errorMessage;
1573 if (!getModuleList(interfaces().debugSymbols, &modules, &errorMessage))
1574 m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
1575 m_engine->modulesHandler()->setModules(modules);
1578 static const char *dumperPrefixC = "dumper";
1580 void CdbEnginePrivate::handleModuleLoad(quint64 offset, const QString &name)
1583 qDebug() << Q_FUNC_INFO << "\n " << offset << name;
1585 // Determine module parameters by offset. The callback has almost all the
1586 // parameters we need with the exception of 'symbolsRead'. Retrieve the
1587 // parameters by offset as to avoid a hack like 'check last module'.
1588 QString errorMessage;
1589 if (getModuleByOffset(interfaces().debugSymbols, offset, &module, &errorMessage)) {
1590 m_engine->modulesHandler()->addModule(module);
1592 m_engine->warning(errorMessage);
1594 m_dumper->moduleLoadHook(name, m_hDebuggeeProcess);
1597 void CdbEnginePrivate::handleModuleUnload(const QString &imageName)
1599 m_engine->modulesHandler()->removeModule(imageName);
1602 void CdbEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
1606 qDebug() << Q_FUNC_INFO;
1607 m_stoppedReason = StoppedBreakpoint;
1608 CdbCore::BreakPoint breakpoint;
1609 // Format message unless it is a temporary step-out breakpoint with empty expression.
1611 if (breakpoint.retrieve(pBP, &expression)) {
1612 expression = breakpoint.expression();
1616 if (!expression.isEmpty())
1617 m_stoppedMessage = breakpoint.type == CdbCore::BreakPoint::Code ?
1618 CdbEngine::tr("Breakpoint: %1").arg(expression) :
1619 CdbEngine::tr("Watchpoint: %1").arg(expression);
1622 void CdbEngine::reloadSourceFiles()
1626 void CdbEngine::syncDebuggerPaths()
1629 qDebug() << Q_FUNC_INFO << m_d->m_options->symbolPaths << m_d->m_options->sourcePaths;
1630 QString errorMessage;
1631 if (!m_d->setSourcePaths(m_d->m_options->sourcePaths, &errorMessage)
1632 || !m_d->setSymbolPaths(m_d->m_options->symbolPaths, &errorMessage)) {
1633 errorMessage = QString::fromLatin1("Unable to set the debugger paths: %1").arg(errorMessage);
1634 warning(errorMessage);
1638 unsigned CdbEngine::debuggerCapabilities() const
1640 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1641 |WatchpointCapability
1642 |BreakOnThrowAndCatchCapability; // Sort-of: Can break on throw().
1645 // Accessed by RunControlFactory
1646 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp)
1649 QString errorMessage;
1650 DebuggerEngine *engine = CdbEngine::create(sp, &errorMessage);
1652 QObject::connect(theOptionsPage, SIGNAL(debuggerPathsChanged()), engine, SLOT(syncDebuggerPaths()));
1654 theOptionsPage->setFailureMessage(errorMessage);
1655 qWarning("%s\n" ,qPrintable(errorMessage));
1660 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
1662 // FIXME: HACK (global variable)
1663 theOptionsPage = new CdbOptionsPage;
1664 opts->push_back(theOptionsPage);
1667 bool checkCdbConfiguration(int toolChainI, QString *errorMsg, QString *settingsPage)
1669 const ProjectExplorer::ToolChain::ToolChainType toolChain = static_cast<ProjectExplorer::ToolChain::ToolChainType>(toolChainI);
1670 switch (toolChain) {
1671 case ProjectExplorer::ToolChain::MinGW: // Do our best
1672 case ProjectExplorer::ToolChain::MSVC:
1673 case ProjectExplorer::ToolChain::WINCE:
1674 case ProjectExplorer::ToolChain::OTHER:
1675 case ProjectExplorer::ToolChain::UNKNOWN:
1676 case ProjectExplorer::ToolChain::INVALID:
1679 *errorMsg = CdbEngine::tr("The CDB debug engine does not support the '%1").
1680 arg(ProjectExplorer::ToolChain::toolChainName(toolChain));
1681 *settingsPage = CdbOptionsPage::settingsId();
1687 } // namespace Internal
1688 } // namespace Debugger