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 "cdbdebugengine.h"
31 #include "cdbdebugengine_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 "debuggeragents.h"
43 #include "debuggeruiswitcher.h"
44 #include "debuggermainwindow.h"
46 #include "debuggeractions.h"
47 #include "debuggermanager.h"
48 #include "breakhandler.h"
49 #include "stackhandler.h"
50 #include "watchhandler.h"
51 #include "registerhandler.h"
52 #include "moduleshandler.h"
53 #include "watchutils.h"
55 #include <coreplugin/icore.h>
56 #include <utils/qtcassert.h>
57 #include <utils/winutils.h>
58 #include <utils/consoleprocess.h>
59 #include <utils/fancymainwindow.h>
60 #include <texteditor/itexteditor.h>
61 #include <utils/savedaction.h>
63 #include <QtCore/QDebug>
64 #include <QtCore/QTimer>
65 #include <QtCore/QTimerEvent>
66 #include <QtCore/QFileInfo>
67 #include <QtCore/QDir>
68 #include <QtCore/QLibrary>
69 #include <QtCore/QCoreApplication>
70 #include <QtGui/QMessageBox>
71 #include <QtGui/QMainWindow>
72 #include <QtGui/QApplication>
73 #include <QtGui/QToolTip>
75 #define DBGHELP_TRANSLATE_TCHAR
76 #include <inc/Dbghelp.h>
78 static const char *localSymbolRootC = "local";
83 typedef QList<WatchData> WatchList;
85 // ----- Message helpers
87 static QString msgStackIndexOutOfRange(int idx, int size)
89 return QString::fromLatin1("Frame index %1 out of range (%2).").arg(idx).arg(size);
92 QString msgDebuggerCommandFailed(const QString &command, HRESULT hr)
94 return QString::fromLatin1("Unable to execute '%1': %2").arg(command, CdbCore::msgDebugEngineComResult(hr));
97 static const char *msgNoStackTraceC = "Internal error: no stack trace present.";
99 // Format function failure message. Pass in Q_FUNC_INFO
100 static QString msgFunctionFailed(const char *func, const QString &why)
102 // Strip a "cdecl_ int namespace1::class::foo(int bar)" as
103 // returned by Q_FUNC_INFO down to "foo"
104 QString function = QLatin1String(func);
105 const int firstParentPos = function.indexOf(QLatin1Char('('));
106 if (firstParentPos != -1)
107 function.truncate(firstParentPos);
108 const int classSepPos = function.lastIndexOf(QLatin1String("::"));
109 if (classSepPos != -1)
110 function.remove(0, classSepPos + 2);
111 //: Function call failed
112 return CdbDebugEngine::tr("The function \"%1()\" failed: %2").arg(function, why);
115 // ----- Engine helpers
117 // --- CdbDebugEnginePrivate
119 CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *manager,
120 const QSharedPointer<CdbOptions> &options,
121 CdbDebugEngine* engine) :
123 m_hDebuggeeProcess(0),
124 m_hDebuggeeThread(0),
125 m_breakEventMode(BreakEventHandle),
126 m_dumper(new CdbDumperHelper(manager, this)),
127 m_currentThreadId(-1),
129 m_interruptArticifialThreadId(-1),
130 m_ignoreInitialBreakPoint(false),
131 m_interrupted(false),
133 m_currentStackTrace(0),
134 m_firstActivatedFrame(true),
135 m_inferiorStartupComplete(false),
137 m_stoppedReason(StoppedOther)
139 connect(this, SIGNAL(watchTimerDebugEvent()), this, SLOT(handleDebugEvent()));
140 connect(this, SIGNAL(modulesLoaded()), this, SLOT(slotModulesLoaded()));
143 bool CdbDebugEnginePrivate::init(QString *errorMessage)
145 enum { bufLen = 10240 };
147 if (!CdbCore::CoreEngine::init(m_options->path, errorMessage))
149 CdbDebugOutput *output = new CdbDebugOutput;
150 setDebugOutput(DebugOutputBasePtr(output));
151 connect(output, SIGNAL(debuggerOutput(int,QString)),
152 manager(), SLOT(showDebuggerOutput(int,QString)));
153 connect(output, SIGNAL(debuggerInputPrompt(int,QString)),
154 manager(), SLOT(showDebuggerInput(int,QString)));
155 connect(output, SIGNAL(debuggeeOutput(QString,bool)),
156 manager(), SLOT(showApplicationOutput(QString,bool)));
157 connect(output, SIGNAL(debuggeeInputPrompt(QString,bool)),
158 manager(), SLOT(showApplicationOutput(QString,bool)));
160 setDebugEventCallback(DebugEventCallbackBasePtr(new CdbDebugEventCallback(m_engine)));
166 IDebuggerEngine *CdbDebugEngine::create(Debugger::DebuggerManager *manager,
167 const QSharedPointer<CdbOptions> &options,
168 QString *errorMessage)
170 CdbDebugEngine *rc = new CdbDebugEngine(manager, options);
171 if (rc->m_d->init(errorMessage)) {
172 rc->syncDebuggerPaths();
179 void CdbDebugEnginePrivate::updateCodeLevel()
181 const CdbCore::CoreEngine::CodeLevel cl = theDebuggerBoolSetting(OperateByInstruction) ?
182 CdbCore::CoreEngine::CodeLevelAssembly : CdbCore::CoreEngine::CodeLevelSource;
186 CdbDebugEnginePrivate::~CdbDebugEnginePrivate()
191 DebuggerManager *CdbDebugEnginePrivate::manager() const
193 return m_engine->manager();
196 void CdbDebugEnginePrivate::clearForRun()
199 qDebug() << Q_FUNC_INFO;
201 m_breakEventMode = BreakEventHandle;
202 m_eventThreadId = m_interruptArticifialThreadId = -1;
203 m_interrupted = false;
205 m_stoppedReason = StoppedOther;
206 m_stoppedMessage.clear();
209 void CdbDebugEnginePrivate::cleanStackTrace()
211 if (m_currentStackTrace) {
212 delete m_currentStackTrace;
213 m_currentStackTrace = 0;
215 m_firstActivatedFrame = false;
216 m_editorToolTipCache.clear();
219 CdbDebugEngine::CdbDebugEngine(DebuggerManager *manager, const QSharedPointer<CdbOptions> &options) :
220 IDebuggerEngine(manager),
221 m_d(new CdbDebugEnginePrivate(manager, options, this))
223 m_d->m_consoleStubProc.setMode(Utils::ConsoleProcess::Suspend);
224 connect(&m_d->m_consoleStubProc, SIGNAL(processMessage(QString,bool)),
225 this, SLOT(slotConsoleStubMessage(QString, bool)));
226 connect(&m_d->m_consoleStubProc, SIGNAL(processStarted()),
227 this, SLOT(slotConsoleStubStarted()));
228 connect(&m_d->m_consoleStubProc, SIGNAL(wrapperStopped()),
229 this, SLOT(slotConsoleStubTerminated()));
232 CdbDebugEngine::~CdbDebugEngine()
237 void CdbDebugEngine::setState(DebuggerState state, const char *func, int line)
240 qDebug() << "setState(" << state << ") at " << func << ':' << line;
241 IDebuggerEngine::setState(state);
244 void CdbDebugEngine::shutdown()
249 QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &function)
251 // Figure the editor tooltip. Ask the frame context of the
252 // function if it is a local variable it knows. If that is not
253 // the case, try to evaluate via debugger
254 QString errorMessage;
256 // Find the frame of the function if there is any
257 CdbSymbolGroupContext *frame = 0;
258 if (m_d->m_currentStackTrace && !function.isEmpty()) {
259 const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
261 qDebug() << "CdbDebugEngine::editorToolTip" << exp << function << frameIndex;
262 if (frameIndex != -1)
263 frame = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
265 if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
267 // No function/symbol context found, try to evaluate in current context.
268 // Do not append type as this will mostly be 'long long' for integers, etc.
271 qDebug() << "Defaulting to expression";
272 if (!m_d->evaluateExpression(exp, &rc, &type, &errorMessage))
277 void CdbDebugEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
279 typedef CdbDebugEnginePrivate::EditorToolTipCache EditorToolTipCache;
281 qDebug() << Q_FUNC_INFO << '\n' << cursorPos;
282 // Need a stopped debuggee and a cpp file
283 if (!m_d->m_hDebuggeeProcess || m_d->isDebuggeeRunning())
285 if (!isCppEditor(editor))
287 // Determine expression and function
293 const QString exp = cppExpressionAt(editor, cursorPos, &line, &column, &function);
294 if (function.isEmpty() || exp.isEmpty())
296 // Check cache (key containing function) or try to figure out expression
297 QString cacheKey = function;
298 cacheKey += QLatin1Char('@');
300 const EditorToolTipCache::const_iterator cit = m_d->m_editorToolTipCache.constFind(cacheKey);
301 if (cit != m_d->m_editorToolTipCache.constEnd()) {
302 toolTip = cit.value();
304 toolTip = editorToolTip(exp, function);
305 if (!toolTip.isEmpty())
306 m_d->m_editorToolTipCache.insert(cacheKey, toolTip);
310 QToolTip::hideText();
311 if (!toolTip.isEmpty())
312 QToolTip::showText(mousePos, toolTip);
315 void CdbDebugEnginePrivate::clearDisplay()
317 manager()->threadsHandler()->removeAll();
318 manager()->modulesHandler()->removeAll();
319 manager()->registerHandler()->removeAll();
322 void CdbDebugEnginePrivate::checkVersion()
324 static bool versionNotChecked = true;
325 // Check for version 6.11 (extended expression syntax)
326 if (versionNotChecked) {
327 versionNotChecked = false;
328 // Get engine DLL version
329 QString errorMessage;
330 const QString version = Utils::winGetDLLVersion(Utils::WinDLLProductVersion, dbengDLL(), &errorMessage);
331 if (version.isEmpty()) {
332 qWarning("%s\n", qPrintable(errorMessage));
336 const double minVersion = 6.11;
337 manager()->showDebuggerOutput(LogMisc, CdbDebugEngine::tr("Version: %1").arg(version));
338 if (version.toDouble() < minVersion) {
339 const QString msg = CdbDebugEngine::tr(
340 "<html>The installed version of the <i>Debugging Tools for Windows</i> (%1) "
341 "is rather old. Upgrading to version %2 is recommended "
342 "for the proper display of Qt's data types.</html>").arg(version).arg(minVersion);
343 Core::ICore::instance()->showWarningWithOptions(CdbDebugEngine::tr("Debugger"), msg, QString(),
344 QLatin1String(Constants::DEBUGGER_SETTINGS_CATEGORY),
345 CdbOptionsPage::settingsId());
350 void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp)
352 if (debugCDBExecution)
353 qDebug() << "startDebugger" << *sp;
354 CdbCore::BreakPoint::clearNormalizeFileNameCache();
355 setState(AdapterStarting, Q_FUNC_INFO, __LINE__);
357 if (m_d->m_hDebuggeeProcess) {
358 warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged."));
359 setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__);
363 switch (sp->startMode) {
366 warning(QLatin1String("Internal error: Mode not supported."));
367 setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__);
373 m_d->m_mode = sp->startMode;
375 m_d->m_inferiorStartupComplete = false;
376 setState(AdapterStarted, Q_FUNC_INFO, __LINE__);
378 QString errorMessage;
379 if (!m_d->setBreakOnThrow(theDebuggerBoolSetting(BreakOnThrow), &errorMessage))
380 manager()->showDebuggerOutput(LogWarning, errorMessage);
381 m_d->setVerboseSymbolLoading(m_d->m_options->verboseSymbolLoading);
382 // Figure out dumper. @TODO: same in gdb...
383 const QString dumperLibName = QDir::toNativeSeparators(manager()->qtDumperLibraryName());
384 bool dumperEnabled = m_d->m_mode != AttachCore
385 && m_d->m_mode != AttachCrashedExternal
386 && manager()->qtDumperLibraryEnabled();
388 const QFileInfo fi(dumperLibName);
390 const QStringList &locations = manager()->qtDumperLibraryLocations();
391 const QString loc = locations.join(QLatin1String(", "));
392 const QString msg = tr("The dumper library was not found at %1.").arg(loc);
393 manager()->showQtDumperLibraryWarning(msg);
394 dumperEnabled = false;
397 m_d->m_dumper->reset(dumperLibName, dumperEnabled);
399 setState(InferiorStarting, Q_FUNC_INFO, __LINE__);
400 manager()->showStatusMessage("Starting Debugger", messageTimeOut);
403 bool needWatchTimer = false;
405 m_d->updateCodeLevel();
406 m_d->m_ignoreInitialBreakPoint = false;
407 switch (m_d->m_mode) {
409 case AttachCrashedExternal:
410 rc = startAttachDebugger(sp->attachPID, m_d->m_mode, &errorMessage);
411 needWatchTimer = true; // Fetch away module load, etc. even if crashed
415 if (sp->useTerminal) {
416 // Attaching to console processes triggers an initial breakpoint, which we do not want
417 m_d->m_ignoreInitialBreakPoint = true;
418 // Launch console stub and wait for its startup
419 m_d->m_consoleStubProc.stop(); // We leave the console open, so recycle it now.
420 m_d->m_consoleStubProc.setWorkingDirectory(sp->workingDirectory);
421 m_d->m_consoleStubProc.setEnvironment(sp->environment);
422 rc = m_d->m_consoleStubProc.start(sp->executable, sp->processArgs);
424 errorMessage = tr("The console stub process was unable to start '%1'.").arg(sp->executable);
425 // continues in slotConsoleStubStarted()...
427 needWatchTimer = true;
428 rc = m_d->startDebuggerWithExecutable(sp->workingDirectory,
436 errorMessage = tr("Attaching to core files is not supported!");
441 m_d->startWatchTimer();
442 emit startSuccessful();
444 warning(errorMessage);
445 setState(InferiorStartFailed, Q_FUNC_INFO, __LINE__);
450 bool CdbDebugEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage)
452 // Need to attach invasively, otherwise, no notification signals
453 // for for CreateProcess/ExitProcess occur.
454 // Initial breakpoint occur:
455 // 1) Desired: When attaching to a crashed process
456 // 2) Undesired: When starting up a console process, in conjunction
457 // with the 32bit Wow-engine
458 // As of version 6.11, the flag only affects 1). 2) Still needs to be suppressed
459 // by lookup at the state of the application (startup trap). However,
460 // there is no startup trap when attaching to a process that has been
461 // running for a while. (see notifyException).
462 const bool suppressInitialBreakPoint = sm != AttachCrashedExternal;
463 return m_d->startAttachDebugger(pid, suppressInitialBreakPoint, errorMessage);
466 void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle)
468 m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__);
469 setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle));
470 ULONG currentThreadId;
471 if (SUCCEEDED(interfaces().debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, ¤tThreadId))) {
472 m_currentThreadId = currentThreadId;
474 m_currentThreadId = 0;
476 // Clear any saved breakpoints and set initial breakpoints
477 m_engine->executeDebuggerCommand(QLatin1String("bc"));
478 if (manager()->breakHandler()->hasPendingBreakpoints()) {
479 if (debugCDBExecution)
480 qDebug() << "processCreatedAttached: Syncing breakpoints";
481 m_engine->attemptBreakpointSynchronization();
483 // Attaching to crashed: This handshake (signalling an event) is required for
484 // the exception to be delivered to the debugger
485 // Also, see special handling in slotModulesLoaded().
486 if (m_mode == AttachCrashedExternal) {
487 const QString crashParameter = manager()->startParameters()->crashParameter;
488 if (!crashParameter.isEmpty()) {
489 ULONG64 evtNr = crashParameter.toULongLong();
490 const HRESULT hr = interfaces().debugControl->SetNotifyEventHandle(evtNr);
492 m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(CdbCore::msgComFailed("SetNotifyEventHandle", hr)));
495 m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
496 if (debugCDBExecution)
497 qDebug() << "<processCreatedAttached";
500 void CdbDebugEngine::processTerminated(unsigned long exitCode)
502 manager()->showDebuggerOutput(LogMisc, tr("The process exited with exit code %1.").arg(exitCode));
503 if (state() != InferiorStopping)
504 setState(InferiorStopping, Q_FUNC_INFO, __LINE__);
505 setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
506 setState(InferiorShuttingDown, Q_FUNC_INFO, __LINE__);
507 m_d->setDebuggeeHandles(0, 0);
509 setState(InferiorShutDown, Q_FUNC_INFO, __LINE__);
510 // Avoid calls from event handler.
511 QTimer::singleShot(0, manager(), SLOT(exitDebugger()));
514 bool CdbDebugEnginePrivate::endInferior(EndInferiorAction action, QString *errorMessage)
516 // Process must be stopped in order to terminate
517 m_engine->setState(InferiorShuttingDown, Q_FUNC_INFO, __LINE__); // pretend it is shutdown
518 const bool wasRunning = isDebuggeeRunning();
520 interruptInterferiorProcess(errorMessage);
521 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
523 bool success = false;
526 if (detachCurrentProcess(errorMessage))
529 case TerminateInferior:
531 // The exit process event handler will not be called.
532 terminateCurrentProcess(errorMessage);
537 if (terminateProcesses(errorMessage))
540 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
543 // Perform cleanup even when failed..no point clinging to the process
544 setDebuggeeHandles(0, 0);
546 m_engine->setState(success ? InferiorShutDown : InferiorShutdownFailed, Q_FUNC_INFO, __LINE__);
550 // End debugging. Note that this can invoked via user action
551 // or the processTerminated() event handler, in which case it
552 // must not kill the process again.
553 void CdbDebugEnginePrivate::endDebugging(EndDebuggingMode em)
556 qDebug() << Q_FUNC_INFO << em;
558 const DebuggerState oldState = m_engine->state();
559 if (oldState == DebuggerNotReady || m_mode == AttachCore)
561 // Do we need to stop the process?
562 QString errorMessage;
563 if (oldState != InferiorShutDown && m_hDebuggeeProcess) {
564 EndInferiorAction action;
566 case EndDebuggingAuto:
567 action = (m_mode == AttachExternal || m_mode == AttachCrashedExternal) ?
568 DetachInferior : TerminateInferior;
570 case EndDebuggingDetach:
571 action = DetachInferior;
573 case EndDebuggingTerminate:
574 action = TerminateInferior;
578 qDebug() << Q_FUNC_INFO << action;
579 // Need a stopped debuggee to act
580 if (!endInferior(action, &errorMessage)) {
581 errorMessage = QString::fromLatin1("Unable to detach from/end the debuggee: %1").arg(errorMessage);
582 manager()->showDebuggerOutput(LogError, errorMessage);
584 errorMessage.clear();
586 // Clean up resources (open files, etc.)
587 m_engine->setState(EngineShuttingDown, Q_FUNC_INFO, __LINE__);
589 const bool endedCleanly = endSession(&errorMessage);
590 m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__);
592 errorMessage = QString::fromLatin1("There were errors trying to end debugging:\n%1").arg(errorMessage);
593 manager()->showDebuggerOutput(LogError, errorMessage);
597 void CdbDebugEngine::exitDebugger()
602 void CdbDebugEngine::detachDebugger()
604 m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach);
607 CdbSymbolGroupContext *CdbDebugEnginePrivate::getSymbolGroupContext(int frameIndex, QString *errorMessage) const
609 if (!m_currentStackTrace) {
610 *errorMessage = QLatin1String(msgNoStackTraceC);
613 if (CdbSymbolGroupContext *sg = m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, errorMessage))
618 void CdbDebugEngine::evaluateWatcher(WatchData *wd)
620 if (debugCDBWatchHandling)
621 qDebug() << Q_FUNC_INFO << wd->exp;
622 QString errorMessage;
625 QString exp = wd->exp;
626 // Remove locals watch prefix.
627 if (exp.startsWith(QLatin1String("local.")))
629 if (m_d->evaluateExpression(exp, &value, &type, &errorMessage)) {
633 wd->setValue(errorMessage);
634 wd->setTypeUnneeded();
636 wd->setHasChildren(false);
639 void CdbDebugEngine::updateWatchData(const WatchData &incomplete)
641 // Watch item was edited while running
642 if (m_d->isDebuggeeRunning())
645 if (debugCDBWatchHandling)
646 qDebug() << Q_FUNC_INFO << "\n " << incomplete.toString();
648 WatchHandler *watchHandler = manager()->watchHandler();
649 if (incomplete.iname.startsWith("watch.")) {
650 WatchData watchData = incomplete;
651 evaluateWatcher(&watchData);
652 watchHandler->insertData(watchData);
656 const int frameIndex = manager()->stackHandler()->currentIndex();
658 bool success = false;
659 QString errorMessage;
661 CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->cdbSymbolGroupContextAt(frameIndex, &errorMessage);
664 if (!sg->completeData(incomplete, watchHandler, &errorMessage))
669 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
670 if (debugCDBWatchHandling > 1)
671 qDebug() << *manager()->watchHandler()->model(LocalsWatch);
674 // Continue inferior with a debugger command, such as "p", "pt"
675 // or its thread variations
676 bool CdbDebugEnginePrivate::executeContinueCommand(const QString &command)
679 qDebug() << Q_FUNC_INFO << command;
681 updateCodeLevel(); // Step by instruction
682 m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__);
683 manager()->showDebuggerOutput(LogMisc, CdbDebugEngine::tr("Continuing with '%1'...").arg(command));
684 QString errorMessage;
685 const bool success = executeDebuggerCommand(command, &errorMessage);
687 m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
690 m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
691 m_engine->warning(CdbDebugEngine::tr("Unable to continue: %1").arg(errorMessage));
696 static inline QString msgStepFailed(unsigned long executionStatus, int threadId, const QString &why)
698 if (executionStatus == DEBUG_STATUS_STEP_OVER)
699 return QString::fromLatin1("Thread %1: Unable to step over: %2").arg(threadId).arg(why);
700 return QString::fromLatin1("Thread %1: Unable to step into: %2").arg(threadId).arg(why);
703 // Step out has to be done via executing 'gu'. TODO: Remove once it is
704 // accessible via normal API for SetExecutionStatus().
706 enum { CdbExtendedExecutionStatusStepOut = 7452347 };
708 // Step with DEBUG_STATUS_STEP_OVER ('p'-command),
709 // DEBUG_STATUS_STEP_INTO ('t'-trace-command) or
710 // CdbExtendedExecutionStatusStepOut ("gu"-command)
711 // its reverse equivalents in the case of single threads.
713 bool CdbDebugEngine::step(unsigned long executionStatus)
715 if (debugCDBExecution)
716 qDebug() << ">step" << executionStatus << "curr " << m_d->m_currentThreadId << " evt " << m_d->m_eventThreadId;
718 // State of reverse stepping as of 10/2009 (Debugging tools 6.11@404):
719 // The constants exist, but invoking the calls leads to E_NOINTERFACE.
720 // Also there is no CDB command for it.
721 if (executionStatus == DEBUG_STATUS_REVERSE_STEP_OVER || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO) {
722 warning(tr("Reverse stepping is not implemented."));
726 // Do not step the artifical thread created to interrupt the debuggee.
727 if (m_d->m_interrupted && m_d->m_currentThreadId == m_d->m_interruptArticifialThreadId) {
728 warning(tr("Thread %1 cannot be stepped.").arg(m_d->m_currentThreadId));
732 // SetExecutionStatus() continues the thread that triggered the
733 // stop event (~# p). This can be confusing if the user is looking
734 // at the stack trace of another thread and wants to step that one. If that
735 // is the case, explicitly tell it to step the current thread using a command.
736 const int triggeringEventThread = m_d->m_eventThreadId;
737 const bool sameThread = triggeringEventThread == -1
738 || m_d->m_currentThreadId == triggeringEventThread
739 || manager()->threadsHandler()->threads().size() == 1;
740 m_d->clearForRun(); // clears thread ids
741 m_d->updateCodeLevel(); // Step by instruction or source line
742 setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__);
743 bool success = false;
744 if (sameThread && executionStatus != CdbExtendedExecutionStatusStepOut) { // Step event-triggering thread, use fast API
745 const HRESULT hr = m_d->interfaces().debugControl->SetExecutionStatus(executionStatus);
746 success = SUCCEEDED(hr);
748 warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, CdbCore::msgComFailed("SetExecutionStatus", hr)));
750 // Need to use a command to explicitly specify the current thread
752 QTextStream str(&command);
753 str << '~' << m_d->m_currentThreadId << ' ';
754 switch (executionStatus) {
755 case DEBUG_STATUS_STEP_OVER:
758 case DEBUG_STATUS_STEP_INTO:
761 case CdbExtendedExecutionStatusStepOut:
765 manager()->showDebuggerOutput(tr("Stepping %1").arg(command));
766 const HRESULT hr = m_d->interfaces().debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, command.toLatin1().constData(), DEBUG_EXECUTE_ECHO);
767 success = SUCCEEDED(hr);
769 warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, msgDebuggerCommandFailed(command, hr)));
772 // Oddity: Step into will first break at the calling function. Ignore
773 if (executionStatus == DEBUG_STATUS_STEP_INTO || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO)
774 m_d->m_breakEventMode = CdbDebugEnginePrivate::BreakEventIgnoreOnce;
775 m_d->startWatchTimer();
776 setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
778 setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
780 if (debugCDBExecution)
781 qDebug() << "<step samethread" << sameThread << "succeeded" << success;
785 void CdbDebugEngine::executeStep()
787 step(manager()->isReverseDebugging() ? DEBUG_STATUS_REVERSE_STEP_INTO : DEBUG_STATUS_STEP_INTO);
790 void CdbDebugEngine::executeNext()
792 step(manager()->isReverseDebugging() ? DEBUG_STATUS_REVERSE_STEP_OVER : DEBUG_STATUS_STEP_OVER);
795 void CdbDebugEngine::executeStepI()
797 executeStep(); // Step into by instruction (figured out by step)
800 void CdbDebugEngine::executeNextI()
802 executeNext(); // Step over by instruction (figured out by step)
805 void CdbDebugEngine::executeStepOut()
807 if (!manager()->isReverseDebugging())
808 step(CdbExtendedExecutionStatusStepOut);
811 void CdbDebugEngine::continueInferior()
813 QString errorMessage;
814 if (!m_d->continueInferior(&errorMessage))
815 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
818 // Continue process without notifications
819 bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* = 0 */)
821 if (debugCDBExecution)
822 qDebug() << "continueInferiorProcess";
823 const HRESULT hr = interfaces().debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
825 const QString errorMessage = CdbCore::msgComFailed("SetExecutionStatus", hr);
826 if (errorMessagePtr) {
827 *errorMessagePtr = errorMessage;
829 m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
836 // Continue process with notifications
837 bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage)
839 // Check state: Are we running?
840 const ULONG ex = executionStatus();
842 qDebug() << Q_FUNC_INFO << "\n ex=" << ex;
844 if (ex == DEBUG_STATUS_GO) {
845 m_engine->warning(QLatin1String("continueInferior() called while debuggee is running."));
849 m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__);
850 bool success = false;
855 manager()->resetLocation();
856 manager()->showStatusMessage(CdbDebugEngine::tr("Running requested..."), messageTimeOut);
858 if (!continueInferiorProcess(errorMessage))
865 m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
867 m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__); // No RunningRequestFailed?
872 bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
875 // Interrupt the interferior process without notifications
876 // Could use setInterrupt, but that does not work.
877 if (debugCDBExecution) {
878 qDebug() << "interruptInterferiorProcess ex=" << executionStatus();
880 const bool rc = debugBreakProcess(m_hDebuggeeProcess, errorMessage);
882 m_interrupted = true;
886 void CdbDebugEnginePrivate::slotModulesLoaded()
888 // Attaching to crashed windows processes: Unless QtCreator is
889 // spawned by the debug handler and inherits the handles,
890 // the event handling does not work reliably (that is, the crash
891 // event is not delivered). In that case, force a break
892 if (m_mode == AttachCrashedExternal && m_engine->state() != InferiorStopped)
893 QTimer::singleShot(10, m_engine, SLOT(slotBreakAttachToCrashed()));
896 void CdbDebugEngine::slotBreakAttachToCrashed()
898 // Force a break when attaching to crashed process (if Creator was not spawned
900 if (state() != InferiorStopped) {
901 manager()->showDebuggerOutput(LogMisc, QLatin1String("Forcing break..."));
902 m_d->m_dumper->disable();
907 void CdbDebugEngine::interruptInferior()
909 if (!m_d->m_hDebuggeeProcess || !m_d->isDebuggeeRunning())
912 QString errorMessage;
913 setState(InferiorStopping, Q_FUNC_INFO, __LINE__);
914 if (!m_d->interruptInterferiorProcess(&errorMessage)) {
915 setState(InferiorStopFailed, Q_FUNC_INFO, __LINE__);
916 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
920 void CdbDebugEngine::executeRunToLine(const QString &fileName, int lineNumber)
922 manager()->showDebuggerOutput(LogMisc, tr("Running up to %1:%2...").arg(fileName).arg(lineNumber));
923 QString errorMessage;
924 CdbCore::BreakPoint tempBreakPoint;
925 tempBreakPoint.fileName = fileName;
926 tempBreakPoint.lineNumber = lineNumber;
927 tempBreakPoint.oneShot = true;
928 const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage)
929 && m_d->continueInferior(&errorMessage);
931 warning(errorMessage);
934 void CdbDebugEngine::executeRunToFunction(const QString &functionName)
936 manager()->showDebuggerOutput(LogMisc, tr("Running up to function '%1()'...").arg(functionName));
937 QString errorMessage;
938 CdbCore::BreakPoint tempBreakPoint;
939 tempBreakPoint.funcName = functionName;
940 tempBreakPoint.oneShot = true;
941 const bool ok = tempBreakPoint.add(m_d->interfaces().debugControl, &errorMessage)
942 && m_d->continueInferior(&errorMessage);
944 warning(errorMessage);
947 void CdbDebugEngine::executeJumpToLine(const QString & /* fileName */, int /*lineNumber*/)
949 warning(tr("Jump to line is not implemented"));
952 void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &value)
955 qDebug() << Q_FUNC_INFO << expr << value;
956 const int frameIndex = manager()->stackHandler()->currentIndex();
957 QString errorMessage;
958 bool success = false;
961 CdbSymbolGroupContext *sg = m_d->getSymbolGroupContext(frameIndex, &errorMessage);
964 if (!sg->assignValue(expr, value, &newValue, &errorMessage))
967 WatchHandler *watchHandler = manager()->watchHandler();
968 if (WatchData *fwd = watchHandler->findItem(expr.toLatin1())) {
969 fwd->setValue(newValue);
970 watchHandler->insertData(*fwd);
971 watchHandler->updateWatchers();
976 const QString msg = tr("Unable to assign the value '%1' to '%2': %3").arg(value, expr, errorMessage);
981 void CdbDebugEngine::executeDebuggerCommand(const QString &command)
983 QString errorMessage;
984 if (!m_d->executeDebuggerCommand(command, &errorMessage))
985 warning(errorMessage);
988 void CdbDebugEngine::activateFrame(int frameIndex)
991 qDebug() << Q_FUNC_INFO << frameIndex;
993 if (state() != InferiorStopped) {
994 qWarning("WARNING %s: invoked while debuggee is running\n", Q_FUNC_INFO);
998 QString errorMessage;
999 bool success = false;
1000 StackHandler *stackHandler = manager()->stackHandler();
1002 WatchHandler *watchHandler = manager()->watchHandler();
1003 const int oldIndex = stackHandler->currentIndex();
1004 if (frameIndex >= stackHandler->stackSize()) {
1005 errorMessage = msgStackIndexOutOfRange(frameIndex, stackHandler->stackSize());
1009 if (oldIndex != frameIndex)
1010 stackHandler->setCurrentIndex(frameIndex);
1012 const StackFrame &frame = stackHandler->currentFrame();
1014 const bool showAssembler = !frame.isUsable();
1015 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1016 watchHandler->beginCycle();
1017 watchHandler->endCycle();
1018 QAction *assemblerAction = theDebuggerAction(OperateByInstruction);
1019 if (!assemblerAction->isChecked())
1020 assemblerAction->trigger();
1025 manager()->gotoLocation(frame, true);
1027 if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
1028 watchHandler->beginCycle();
1029 if (CdbSymbolGroupContext *sgc = m_d->getSymbolGroupContext(frameIndex, &errorMessage))
1030 success = sgc->populateModelInitially(watchHandler, &errorMessage);
1031 watchHandler->endCycle();
1037 const QString msg = QString::fromLatin1("Internal error: activateFrame() failed for frame #%1 of %2, thread %3: %4").
1038 arg(frameIndex).arg(stackHandler->stackSize()).arg(m_d->m_currentThreadId).arg(errorMessage);
1041 m_d->m_firstActivatedFrame = false;
1044 void CdbDebugEngine::selectThread(int index)
1047 qDebug() << Q_FUNC_INFO << index;
1049 //reset location arrow
1050 manager()->resetLocation();
1052 ThreadsHandler *threadsHandler = manager()->threadsHandler();
1053 threadsHandler->setCurrentThread(index);
1054 const int newThreadId = threadsHandler->threads().at(index).id;
1055 if (newThreadId != m_d->m_currentThreadId) {
1056 m_d->m_currentThreadId = threadsHandler->threads().at(index).id;
1057 m_d->updateStackTrace();
1061 void CdbDebugEngine::attemptBreakpointSynchronization()
1063 if (!m_d->m_hDebuggeeProcess) // Sometimes called from the breakpoint Window
1065 QString errorMessage;
1066 if (!m_d->attemptBreakpointSynchronization(&errorMessage))
1067 warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
1070 bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessage)
1072 if (!m_hDebuggeeProcess) {
1073 *errorMessage = QLatin1String("attemptBreakpointSynchronization() called while debugger is not running");
1076 // This is called from
1077 // 1) CreateProcessEvent with the halted engine
1078 // 2) from the break handler, potentially while the debuggee is running
1079 // If the debuggee is running (for which the execution status is
1080 // no reliable indicator), we temporarily halt and have ourselves
1081 // called again from the debug event handler.
1084 const bool wasRunning = !CdbCore::BreakPoint::getBreakPointCount(interfaces().debugControl, &dummy);
1086 qDebug() << Q_FUNC_INFO << "\n Running=" << wasRunning;
1089 const HandleBreakEventMode oldMode = m_breakEventMode;
1090 m_breakEventMode = BreakEventSyncBreakPoints;
1091 if (!interruptInterferiorProcess(errorMessage)) {
1092 m_breakEventMode = oldMode;
1098 QStringList warnings;
1099 const bool ok = synchronizeBreakPoints(interfaces().debugControl,
1100 interfaces().debugSymbols,
1101 manager()->breakHandler(),
1102 errorMessage, &warnings);
1103 if (const int warningsCount = warnings.size())
1104 for (int w = 0; w < warningsCount; w++)
1105 m_engine->warning(warnings.at(w));
1109 void CdbDebugEngine::fetchDisassembler(DisassemblerViewAgent *agent)
1111 StackFrame frame = agent->frame();
1112 enum { ContextLines = 40 };
1114 QString errorMessage;
1118 if (!frame.file.isEmpty())
1119 address = frame.address;
1120 if (address.isEmpty())
1121 address = agent->address();
1123 qDebug() << "fetchDisassembler" << address << " Agent: " << agent->address()
1124 << " Frame" << frame.file << frame.line << frame.address;
1125 if (address.isEmpty()) { // Clear window
1126 agent->setContents(QString());
1130 if (address.startsWith(QLatin1String("0x")))
1131 address.remove(0, 2);
1132 const int addressFieldWith = address.size(); // For the Marker
1134 const ULONG64 offset = address.toULongLong(&ok, 16);
1136 errorMessage = QString::fromLatin1("Internal error: Invalid address for disassembly: '%1'.").arg(agent->address());
1139 QString disassembly;
1140 QApplication::setOverrideCursor(Qt::WaitCursor);
1141 ok = dissassemble(m_d, offset, ContextLines, ContextLines, addressFieldWith, QTextStream(&disassembly), &errorMessage);
1142 QApplication::restoreOverrideCursor();
1145 agent->setContents(disassembly);
1150 agent->setContents(QString());
1151 warning(errorMessage);
1155 void CdbDebugEngine::fetchMemory(MemoryViewAgent *agent, QObject *token, quint64 addr, quint64 length)
1157 if (!m_d->m_hDebuggeeProcess && !length)
1160 QByteArray data(length, '\0');
1161 const HRESULT hr = m_d->interfaces().debugDataSpaces->ReadVirtual(addr, data.data(), length, &received);
1163 warning(tr("Unable to retrieve %1 bytes of memory at 0x%2: %3").
1164 arg(length).arg(addr, 0, 16).arg(CdbCore::msgComFailed("ReadVirtual", hr)));
1167 if (received < length)
1168 data.truncate(received);
1169 agent->addLazyData(token, addr, data);
1172 void CdbDebugEngine::reloadModules()
1176 void CdbDebugEngine::loadSymbols(const QString &moduleName)
1179 qDebug() << Q_FUNC_INFO << moduleName;
1182 void CdbDebugEngine::loadAllSymbols()
1185 qDebug() << Q_FUNC_INFO;
1188 void CdbDebugEngine::requestModuleSymbols(const QString &moduleName)
1191 QString errorMessage;
1192 bool success = false;
1194 if (m_d->isDebuggeeRunning()) {
1195 errorMessage = tr("Cannot retrieve symbols while the debuggee is running.");
1198 if (!getModuleSymbols(m_d->interfaces().debugSymbols, moduleName, &rc, &errorMessage))
1203 warning(errorMessage);
1204 manager()->showModuleSymbols(moduleName, rc);
1207 void CdbDebugEngine::reloadRegisters()
1209 if (state() != InferiorStopped)
1211 const int intBase = 10;
1213 qDebug() << Q_FUNC_INFO << intBase;
1215 QString errorMessage;
1216 const Registers registers = getRegisters(m_d->interfaces().debugControl, m_d->interfaces().debugRegisters, &errorMessage, intBase);
1217 if (registers.isEmpty() && !errorMessage.isEmpty())
1218 warning(msgFunctionFailed("reloadRegisters" , errorMessage));
1219 manager()->registerHandler()->setRegisters(registers);
1222 void CdbDebugEngine::slotConsoleStubStarted()
1224 const qint64 appPid = m_d->m_consoleStubProc.applicationPID();
1226 qDebug() << Q_FUNC_INFO << appPid;
1227 // Attach to console process
1228 QString errorMessage;
1229 if (startAttachDebugger(appPid, AttachExternal, &errorMessage)) {
1230 m_d->startWatchTimer();
1231 manager()->notifyInferiorPidChanged(appPid);
1233 QMessageBox::critical(DebuggerUISwitcher::instance()->mainWindow(), tr("Debugger Error"), errorMessage);
1237 void CdbDebugEngine::slotConsoleStubMessage(const QString &msg, bool)
1239 QMessageBox::critical(DebuggerUISwitcher::instance()->mainWindow(), tr("Debugger Error"), msg);
1242 void CdbDebugEngine::slotConsoleStubTerminated()
1247 void CdbDebugEngine::warning(const QString &w)
1249 manager()->showDebuggerOutput(LogWarning, w);
1250 qWarning("%s\n", qPrintable(w));
1253 void CdbDebugEnginePrivate::notifyException(long code, bool fatal, const QString &message)
1255 if (debugCDBExecution)
1256 qDebug() << "notifyException code" << code << " fatal=" << fatal;
1257 // Suppress the initial breakpoint that occurs when
1258 // attaching to a console (If a breakpoint is encountered before startup
1259 // is complete, see startAttachDebugger()).
1261 case winExceptionStartupCompleteTrap:
1262 m_inferiorStartupComplete = true;
1264 case EXCEPTION_BREAKPOINT:
1265 if (m_ignoreInitialBreakPoint && !m_inferiorStartupComplete && m_breakEventMode == BreakEventHandle) {
1266 manager()->showDebuggerOutput(LogMisc, CdbDebugEngine::tr("Ignoring initial breakpoint..."));
1267 m_breakEventMode = BreakEventIgnoreOnce;
1271 // Cannot go over crash point to execute calls.
1273 m_dumper->disable();
1274 m_stoppedReason = StoppedCrash;
1275 m_stoppedMessage = message;
1279 static int threadIndexById(const ThreadsHandler *threadsHandler, int id)
1281 const QList<ThreadData> threads = threadsHandler->threads();
1282 const int count = threads.count();
1283 for (int i = 0; i < count; i++)
1284 if (threads.at(i).id == id)
1289 void CdbDebugEnginePrivate::handleDebugEvent()
1291 if (debugCDBExecution)
1292 qDebug() << "handleDebugEvent mode " << m_breakEventMode
1293 << CdbCore::msgExecutionStatusString(executionStatus()) << " interrupt" << m_interrupted
1294 << " startupcomplete" << m_inferiorStartupComplete;
1295 // restore mode and do special handling
1296 const HandleBreakEventMode mode = m_breakEventMode;
1297 m_breakEventMode = BreakEventHandle;
1300 case BreakEventHandle: {
1301 // If this is triggered by breakpoint/crash: Set state to stopping
1302 // to avoid warnings as opposed to interrupt inferior
1303 if (m_engine->state() != InferiorStopping)
1304 m_engine->setState(InferiorStopping, Q_FUNC_INFO, __LINE__);
1305 m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__);
1306 m_eventThreadId = updateThreadList();
1307 m_interruptArticifialThreadId = m_interrupted ? m_eventThreadId : -1;
1308 // Get thread to stop and its index. If avoidable, do not use
1309 // the artifical thread that is created when interrupting,
1310 // use the oldest thread 0 instead.
1311 ThreadsHandler *threadsHandler = manager()->threadsHandler();
1312 m_currentThreadId = m_interrupted ? 0 : m_eventThreadId;
1313 int currentThreadIndex = -1;
1314 m_currentThreadId = -1;
1315 if (m_interrupted) {
1316 m_currentThreadId = 0;
1317 currentThreadIndex = threadIndexById(threadsHandler, m_currentThreadId);
1319 if (!m_interrupted || currentThreadIndex == -1) {
1320 m_currentThreadId = m_eventThreadId;
1321 currentThreadIndex = threadIndexById(threadsHandler, m_currentThreadId);
1323 const QString msg = m_interrupted ?
1324 CdbDebugEngine::tr("Interrupted in thread %1, current thread: %2").arg(m_interruptArticifialThreadId).arg(m_currentThreadId) :
1325 CdbDebugEngine::tr("Stopped, current thread: %1").arg(m_currentThreadId);
1326 manager()->showDebuggerOutput(LogMisc, msg);
1327 const int threadIndex = threadIndexById(threadsHandler, m_currentThreadId);
1328 if (threadIndex != -1)
1329 threadsHandler->setCurrentThread(threadIndex);
1333 case BreakEventIgnoreOnce:
1335 m_interrupted = false;
1337 case BreakEventSyncBreakPoints: {
1338 m_interrupted = false;
1339 // Temp stop to sync breakpoints
1340 QString errorMessage;
1341 attemptBreakpointSynchronization(&errorMessage);
1343 continueInferiorProcess(&errorMessage);
1344 if (!errorMessage.isEmpty())
1345 m_engine->warning(QString::fromLatin1("In handleDebugEvent: %1").arg(errorMessage));
1351 void CdbDebugEnginePrivate::setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread)
1354 qDebug() << Q_FUNC_INFO << hDebuggeeProcess << hDebuggeeThread;
1355 m_hDebuggeeProcess = hDebuggeeProcess;
1356 m_hDebuggeeThread = hDebuggeeThread;
1359 // Set thread in CDB engine
1360 bool CdbDebugEnginePrivate::setCDBThreadId(unsigned long threadId, QString *errorMessage)
1362 ULONG currentThreadId;
1363 HRESULT hr = interfaces().debugSystemObjects->GetCurrentThreadId(¤tThreadId);
1365 *errorMessage = CdbCore::msgComFailed("GetCurrentThreadId", hr);
1368 if (currentThreadId == threadId)
1370 hr = interfaces().debugSystemObjects->SetCurrentThreadId(threadId);
1372 *errorMessage = QString::fromLatin1("Failed to change to from thread %1 to %2: SetCurrentThreadId() failed: %3").
1373 arg(currentThreadId).arg(threadId).arg(CdbCore::msgDebugEngineComResult(hr));
1376 const QString msg = CdbDebugEngine::tr("Changing threads: %1 -> %2").arg(currentThreadId).arg(threadId);
1377 m_engine->showStatusMessage(msg, 500);
1381 ULONG CdbDebugEnginePrivate::updateThreadList()
1384 qDebug() << Q_FUNC_INFO << m_hDebuggeeProcess;
1386 QList<ThreadData> threads;
1387 ULONG currentThreadId;
1388 QString errorMessage;
1389 // When interrupting, an artifical thread with a breakpoint is created.
1390 if (!CdbStackTraceContext::getThreads(interfaces(), &threads, ¤tThreadId, &errorMessage))
1391 m_engine->warning(errorMessage);
1392 manager()->threadsHandler()->setThreads(threads);
1393 return currentThreadId;
1396 // Figure out the thread to run the dumpers in (see notes on.
1397 // CdbDumperHelper). Avoid the artifical threads created by interrupt
1398 // and threads that are in waitFor().
1399 // A stricter version could only use the thread if it is the event
1400 // thread of a step or breakpoint hit (see CdbDebugEnginePrivate::m_interrupted).
1402 static inline unsigned long dumperThreadId(const QList<StackFrame> &frames,
1403 unsigned long currentThread)
1406 return CdbDumperHelper::InvalidDumperCallThread;
1407 switch (CdbCore::StackTraceContext::specialFunction(frames.at(0).from, frames.at(0).function)) {
1408 case CdbCore::StackTraceContext::BreakPointFunction:
1409 case CdbCore::StackTraceContext::WaitFunction:
1410 return CdbDumperHelper::InvalidDumperCallThread;
1414 // Check remaining frames for wait
1415 const int waitCheckDepth = qMin(frames.size(), 5);
1416 for (int f = 1; f < waitCheckDepth; f++) {
1417 if (CdbCore::StackTraceContext::specialFunction(frames.at(f).from, frames.at(f).function)
1418 == CdbCore::StackTraceContext::WaitFunction)
1419 return CdbDumperHelper::InvalidDumperCallThread;
1421 return currentThread;
1424 // Format stop message with all available information.
1425 QString CdbDebugEnginePrivate::stoppedMessage(const StackFrame *topFrame /* = 0 */) const
1429 if (topFrame->isUsable()) {
1430 // Stopped at basename:line
1431 const int lastSlashPos = topFrame->file.lastIndexOf(QLatin1Char('/'));
1432 const QString file = lastSlashPos == -1 ? topFrame->file : topFrame->file.mid(lastSlashPos + 1);
1433 msg = CdbDebugEngine::tr("Stopped at %1:%2 in thread %3.").
1434 arg(file).arg(topFrame->line).arg(m_currentThreadId);
1436 // Somewhere in assembly code.
1437 if (topFrame->function.isEmpty()) {
1438 msg = CdbDebugEngine::tr("Stopped at %1 in thread %2 (missing debug information).").
1439 arg(topFrame->address).arg(m_currentThreadId);
1441 msg = CdbDebugEngine::tr("Stopped at %1 (%2) in thread %3 (missing debug information).").
1442 arg(topFrame->address).arg(topFrame->function).arg(m_currentThreadId);
1446 msg = CdbDebugEngine::tr("Stopped in thread %1 (missing debug information).").arg(m_currentThreadId);
1449 if (!m_stoppedMessage.isEmpty()) {
1450 msg += QLatin1Char(' ');
1451 msg += m_stoppedMessage;
1456 void CdbDebugEnginePrivate::updateStackTrace()
1459 qDebug() << Q_FUNC_INFO;
1460 // Create a new context
1462 QString errorMessage;
1463 m_engine->reloadRegisters();
1464 if (!setCDBThreadId(m_currentThreadId, &errorMessage)) {
1465 m_engine->warning(errorMessage);
1468 m_currentStackTrace =
1469 CdbStackTraceContext::create(m_dumper, &errorMessage);
1470 if (!m_currentStackTrace) {
1471 m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
1474 // Disassembling slows things down a bit. Assembler is still available via menu.
1476 m_engine->reloadDisassembler(); // requires stack trace
1478 const QList<StackFrame> stackFrames = m_currentStackTrace->stackFrames();
1479 // find the first usable frame and select it
1481 const int count = stackFrames.count();
1482 for (int i=0; i < count; ++i)
1483 if (stackFrames.at(i).isUsable()) {
1487 // Format stop message.
1488 const QString stopMessage = stoppedMessage(stackFrames.isEmpty() ? static_cast<const StackFrame *>(0) : &stackFrames.front());
1489 // Set up dumper with a thread (or invalid)
1490 const unsigned long dumperThread = dumperThreadId(stackFrames, m_currentThreadId);
1491 if (debugCDBExecution)
1492 qDebug() << "updateStackTrace() current: " << m_currentThreadId << " dumper=" << dumperThread;
1493 m_dumper->setDumperCallThread(dumperThread);
1495 manager()->stackHandler()->setFrames(stackFrames);
1496 m_firstActivatedFrame = true;
1498 manager()->stackHandler()->setCurrentIndex(current);
1499 m_engine->activateFrame(current);
1501 // Clean out variables
1502 manager()->watchHandler()->beginCycle();
1503 manager()->watchHandler()->endCycle();
1505 manager()->watchHandler()->updateWatchers();
1506 // Show message after a lengthy dumper initialization
1507 manager()->showStatusMessage(stopMessage, 15000);
1510 void CdbDebugEnginePrivate::updateModules()
1512 QList<Module> modules;
1513 QString errorMessage;
1514 if (!getModuleList(interfaces().debugSymbols, &modules, &errorMessage))
1515 m_engine->warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
1516 manager()->modulesHandler()->setModules(modules);
1519 static const char *dumperPrefixC = "dumper";
1521 void CdbDebugEnginePrivate::handleModuleLoad(const QString &name)
1524 qDebug() << Q_FUNC_INFO << "\n " << name;
1525 m_dumper->moduleLoadHook(name, m_hDebuggeeProcess);
1529 void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP)
1533 qDebug() << Q_FUNC_INFO;
1534 m_stoppedReason = StoppedBreakpoint;
1535 CdbCore::BreakPoint breakpoint;
1536 // Format message unless it is a temporary step-out breakpoint with empty expression.
1538 if (breakpoint.retrieve(pBP, &expression)) {
1539 expression = breakpoint.expression();
1543 if (!expression.isEmpty())
1544 m_stoppedMessage = breakpoint.type == CdbCore::BreakPoint::Code ?
1545 CdbDebugEngine::tr("Breakpoint: %1").arg(expression) :
1546 CdbDebugEngine::tr("Watchpoint: %1").arg(expression);
1549 void CdbDebugEngine::reloadSourceFiles()
1553 void CdbDebugEngine::syncDebuggerPaths()
1556 qDebug() << Q_FUNC_INFO << m_d->m_options->symbolPaths << m_d->m_options->sourcePaths;
1557 QString errorMessage;
1558 if (!m_d->setSourcePaths(m_d->m_options->sourcePaths, &errorMessage)
1559 || !m_d->setSymbolPaths(m_d->m_options->symbolPaths, &errorMessage)) {
1560 errorMessage = QString::fromLatin1("Unable to set the debugger paths: %1").arg(errorMessage);
1561 warning(errorMessage);
1565 unsigned CdbDebugEngine::debuggerCapabilities() const
1567 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1568 |WatchpointCapability
1569 |BreakOnThrowAndCatchCapability; // Sort-of: Can break on throw().
1572 // Accessed by DebuggerManager
1573 IDebuggerEngine *createCdbEngine(DebuggerManager *parent,
1574 bool cmdLineEnabled,
1575 QList<Core::IOptionsPage*> *opts)
1577 // Create options page
1578 QSharedPointer<CdbOptions> options(new CdbOptions);
1579 options->fromSettings(Core::ICore::instance()->settings());
1580 CdbOptionsPage *optionsPage = new CdbOptionsPage(options);
1581 opts->push_back(optionsPage);
1582 if (!cmdLineEnabled || !options->enabled)
1585 QString errorMessage;
1586 IDebuggerEngine *engine = CdbDebugEngine::create(parent, options, &errorMessage);
1588 QObject::connect(optionsPage, SIGNAL(debuggerPathsChanged()), engine, SLOT(syncDebuggerPaths()));
1590 optionsPage->setFailureMessage(errorMessage);
1591 qWarning("%s\n" ,qPrintable(errorMessage));
1596 } // namespace Internal
1597 } // namespace Debugger