1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "cdbengine.h"
34 #include "debuggerstartparameters.h"
35 #include "disassemblerlines.h"
36 #include "cdboptions.h"
37 #include "cdboptionspage.h"
38 #include "bytearrayinputstream.h"
39 #include "breakpoint.h"
40 #include "breakhandler.h"
41 #include "stackframe.h"
42 #include "stackhandler.h"
43 #include "watchhandler.h"
44 #include "threadshandler.h"
45 #include "moduleshandler.h"
46 #include "debuggeractions.h"
47 #include "debuggercore.h"
48 #include "registerhandler.h"
49 #include "disassembleragent.h"
50 #include "memoryagent.h"
51 #include "debuggerrunner.h"
52 #include "debuggertooltipmanager.h"
53 #include "cdbparsehelpers.h"
54 #include "watchutils.h"
55 #include "gdb/gdbmi.h"
56 #include "shared/cdbsymbolpathlisteditor.h"
58 #include <coreplugin/icore.h>
59 #include <texteditor/itexteditor.h>
60 #include <projectexplorer/abi.h>
61 #include <projectexplorer/projectexplorerconstants.h>
63 #include <utils/synchronousprocess.h>
64 #include <utils/winutils.h>
65 #include <utils/qtcassert.h>
66 #include <utils/savedaction.h>
67 #include <utils/consoleprocess.h>
69 #include <QtCore/QCoreApplication>
70 #include <QtCore/QFileInfo>
71 #include <QtCore/QDir>
72 #include <QtCore/QDebug>
73 #include <QtCore/QTextStream>
74 #include <QtCore/QDateTime>
75 #include <QtGui/QToolTip>
76 #include <QtGui/QMainWindow>
77 #include <QtGui/QMessageBox>
80 # include <utils/winutils.h>
81 # include "dbgwinutils.h"
86 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
87 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
90 enum { debugLocals = 0 };
91 enum { debugSourceMapping = 0 };
92 enum { debugWatches = 0 };
93 enum { debugBreakpoints = 0 };
96 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
98 # define STATE_DEBUG(state, func, line, notifyFunc)
102 \class Debugger::Internal::CdbEngine
104 Cdb engine version 2: Run the CDB process on pipes and parse its output.
105 The engine relies on a CDB extension Qt Creator provides as an extension
106 library (32/64bit), which is loaded into cdb.exe. It serves to:
109 \o Notify the engine about the state of the debugging session:
111 \o idle: (hooked up with .idle_cmd) debuggee stopped
112 \o accessible: Debuggee stopped, cdb.exe accepts commands
113 \o inaccessible: Debuggee runs, no way to post commands
114 \o session active/inactive: Lost debuggee, terminating.
116 \o Hook up with output/event callbacks and produce formatted output to be able
117 to catch application output and exceptions.
118 \o Provide some extension commands that produce output in a standardized (GDBMI)
119 format that ends up in handleExtensionMessage(), for example:
121 \o pid Return debuggee pid for interrupting.
122 \o locals Print locals from SymbolGroup
123 \o expandLocals Expand locals in symbol group
124 \o registers, modules, threads
128 Debugger commands can be posted by calling:
132 \o postCommand(): Does not expect a reply
133 \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
134 that is captured by enclosing it in special tokens using the 'echo' command and
135 then invokes a callback with a CdbBuiltinCommand structure.
136 \o postExtensionCommand(): Run a command provided by the extension producing
137 one-line output and invoke a callback with a CdbExtensionCommand structure
138 (output is potentially split up in chunks).
143 [Console: The console stub launches the process. On process startup,
144 launchCDB() is called with AttachExternal].
145 setupEngine() calls launchCDB() with the startparameters. The debuggee
146 runs into the initial breakpoint (session idle). EngineSetupOk is
147 notified (inferior still stopped). setupInferior() is then called
148 which does breakpoint synchronization and issues the extension 'pid'
149 command to obtain the inferior pid (which also hooks up the output callbacks).
150 handlePid() notifies notifyInferiorSetupOk.
151 runEngine() is then called which issues 'g' to continue the inferior.
152 Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
153 when the inferior exits (except attach modes).
156 using namespace ProjectExplorer;
161 static const char localsPrefixC[] = "local.";
163 struct MemoryViewCookie
165 explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
166 quint64 addr = 0, quint64 l = 0) :
167 agent(a), editorToken(e), address(addr), length(l)
171 QObject *editorToken;
176 struct MemoryChangeCookie
178 explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
179 address(addr), data(d) {}
185 struct ConditionalBreakPointCookie
187 ConditionalBreakPointCookie(BreakpointId i = 0) : id(i) {}
192 } // namespace Internal
193 } // namespace Debugger
195 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
196 Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
197 Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
202 static inline bool isConsole(const DebuggerStartParameters &sp)
204 return (sp.startMode == StartInternal || sp.startMode == StartExternal)
209 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
211 QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
212 debuggerCore()->mainWindow());
213 mb->setAttribute(Qt::WA_DeleteOnClose);
218 // Base data structure for command queue entries with callback
219 struct CdbCommandBase
221 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
224 CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
225 unsigned nc, const QVariant &cookie);
231 // Continue with another commands as specified in CommandSequenceFlags
232 unsigned commandSequence;
235 CdbCommandBase::CdbCommandBase() :
236 token(0), flags(0), commandSequence(0)
240 CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
241 unsigned nc, const QVariant &c) :
242 token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
246 // Queue entry for builtin commands producing free-format
247 // line-by-line output.
248 struct CdbBuiltinCommand : public CdbCommandBase
250 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
252 CdbBuiltinCommand() {}
253 CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
255 unsigned nc, const QVariant &cookie) :
256 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
260 QByteArray joinedReply() const;
262 CommandHandler handler;
263 QList<QByteArray> reply;
266 QByteArray CdbBuiltinCommand::joinedReply() const
271 answer.reserve(120 * reply.size());
272 foreach (const QByteArray &l, reply) {
279 // Queue entry for Qt Creator extension commands producing one-line
280 // output with success flag and error message.
281 struct CdbExtensionCommand : public CdbCommandBase
283 typedef CdbEngine::ExtensionCommandHandler CommandHandler;
285 CdbExtensionCommand() : success(false) {}
286 CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
288 unsigned nc, const QVariant &cookie) :
289 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
291 CommandHandler handler;
293 QByteArray errorMessage;
297 template <class CommandPtrType>
298 int indexOfCommand(const QList<CommandPtrType> &l, int token)
300 const int count = l.size();
301 for (int i = 0; i < count; i++)
302 if (l.at(i)->token == token)
307 static inline bool validMode(DebuggerStartMode sm)
320 // Accessed by RunControlFactory
321 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
322 DebuggerEngine *masterEngine, QString *errorMessage)
325 CdbOptionsPage *op = CdbOptionsPage::instance();
326 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
327 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
330 return new CdbEngine(sp, masterEngine, op->options());
332 Q_UNUSED(masterEngine)
335 *errorMessage = QString::fromLatin1("Unsupported debug mode");
339 bool isCdbEngineEnabled()
342 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
348 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
350 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
353 static QString cdbBinary(const DebuggerStartParameters &sp)
355 if (!sp.debuggerCommand.isEmpty()) {
356 // Do not use a GDB binary if we got started for a project with MinGW runtime.
357 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
358 && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
359 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
360 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
362 return sp.debuggerCommand;
364 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
367 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
370 if (!isCdbEngineEnabled()) {
371 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
372 arg(sp.toolChainAbi.toString()));
373 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
374 check->settingsPage = CdbOptionsPage::settingsId();
378 if (!validMode(sp.startMode)) {
379 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
383 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
384 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
385 arg(sp.toolChainAbi.toString()));
389 if (cdbBinary(sp).isEmpty()) {
390 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
391 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
392 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
399 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
404 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
407 opts->push_back(new CdbOptionsPage);
413 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
415 static inline Utils::SavedAction *theAssemblerAction()
417 return debuggerCore()->action(OperateByInstruction);
420 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
421 DebuggerEngine *masterEngine, const OptionsPtr &options) :
422 DebuggerEngine(sp, masterEngine),
423 m_creatorExtPrefix("<qtcreatorcdbext>|"),
424 m_tokenPrefix("<token>"),
426 m_effectiveStartMode(NoStartMode),
429 m_specialStopMode(NoSpecialStop),
430 m_nextCommandToken(0),
431 m_currentBuiltinCommandIndex(-1),
432 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
433 m_operateByInstructionPending(true),
434 m_operateByInstruction(true), // Default CDB setting
435 m_notifyEngineShutdownOnTermination(false),
436 m_hasDebuggee(false),
438 m_sourceStepInto(false),
441 m_ignoreCdbOutput(false)
443 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
445 setObjectName(QLatin1String("CdbEngine"));
446 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
447 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
448 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
449 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
452 void CdbEngine::init()
454 m_effectiveStartMode = NoStartMode;
456 m_accessible = false;
457 m_specialStopMode = NoSpecialStop;
458 m_nextCommandToken = 0;
459 m_currentBuiltinCommandIndex = -1;
460 m_operateByInstructionPending = theAssemblerAction()->isChecked();
461 m_operateByInstruction = true; // Default CDB setting
462 m_notifyEngineShutdownOnTermination = false;
463 m_hasDebuggee = false;
464 m_sourceStepInto = false;
465 m_watchPointX = m_watchPointY = 0;
466 m_ignoreCdbOutput = false;
468 m_outputBuffer.clear();
469 m_builtinCommandQueue.clear();
470 m_extensionCommandQueue.clear();
471 m_extensionMessageBuffer.clear();
472 m_pendingBreakpointMap.clear();
473 m_customSpecialStopData.clear();
475 // Create local list of mappings in native separators
476 m_sourcePathMappings.clear();
477 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
478 if (!globalOptions->sourcePathMap.isEmpty()) {
479 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
480 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
481 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
482 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
483 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
484 QDir::toNativeSeparators(it.value())));
487 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
490 CdbEngine::~CdbEngine()
494 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
496 // To be set next time session becomes accessible
497 m_operateByInstructionPending = operateByInstruction;
498 if (state() == InferiorStopOk)
499 syncOperateByInstruction(operateByInstruction);
502 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
505 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
506 if (m_operateByInstruction == operateByInstruction)
508 QTC_ASSERT(m_accessible, return; )
509 m_operateByInstruction = operateByInstruction;
510 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
511 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
514 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
515 TextEditor::ITextEditor *editor,
516 const DebuggerToolTipContext &contextIn)
519 qDebug() << Q_FUNC_INFO;
520 // Need a stopped debuggee and a cpp file in a valid frame
521 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
523 // Determine expression and function
526 DebuggerToolTipContext context = contextIn;
527 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
528 // Are we in the current stack frame
529 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
531 // No numerical or any other expressions [yet]
532 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
534 // Can this be found as a local variable?
535 const QByteArray localsPrefix(localsPrefixC);
536 QByteArray iname = localsPrefix + exp.toAscii();
537 QModelIndex index = watchHandler()->itemIndex(iname);
538 if (!index.isValid()) {
539 // Nope, try a 'local.this.m_foo'.
540 exp.prepend(QLatin1String("this."));
541 iname.insert(localsPrefix.size(), "this.");
542 index = watchHandler()->itemIndex(iname);
543 if (!index.isValid())
546 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
547 tw->setContext(context);
548 tw->setDebuggerModel(LocalsWatch);
549 tw->setExpression(exp);
550 tw->acquireEngine(this);
551 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
555 // Determine full path to the CDB extension library.
556 QString CdbEngine::extensionLibraryName(bool is64Bit)
558 // Determine extension lib name and path to use
560 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
561 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
562 << '/' << QT_CREATOR_CDB_EXT << ".dll";
566 // Determine environment for CDB.exe, start out with run config and
567 // add CDB extension path merged with system value should there be one.
568 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
569 QString cdbExtensionPath)
571 // Determine CDB extension path from Qt Creator
572 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
573 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
574 if (!oldCdbExtensionPath.isEmpty()) {
575 cdbExtensionPath.append(QLatin1Char(';'));
576 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
578 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
579 // config, just to make sure, delete any existing entries
580 const QString cdbExtensionPathVariableAssign =
581 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
582 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
583 if (it->startsWith(cdbExtensionPathVariableAssign)) {
584 it = runConfigEnvironment.erase(it);
590 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
591 QDir::toNativeSeparators(cdbExtensionPath));
592 return runConfigEnvironment;
595 int CdbEngine::elapsedLogTime() const
597 const int elapsed = m_logTime.elapsed();
598 const int delta = elapsed - m_elapsedLogTime;
599 m_elapsedLogTime = elapsed;
603 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
604 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
607 qDebug("startConsole %s", qPrintable(sp.executable));
608 m_consoleStub.reset(new Utils::ConsoleProcess);
609 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
610 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
611 SLOT(consoleStubMessage(QString, bool)));
612 connect(m_consoleStub.data(), SIGNAL(processStarted()),
613 SLOT(consoleStubProcessStarted()));
614 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
615 SLOT(consoleStubExited()));
616 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
617 if (sp.environment.size())
618 m_consoleStub->setEnvironment(sp.environment);
619 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
620 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
626 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
629 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
631 if (state() == EngineSetupRequested) {
632 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
633 notifyEngineSetupFailed();
635 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
638 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
640 showMessage(msg, AppOutput);
644 void CdbEngine::consoleStubProcessStarted()
647 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
648 // Attach to console process.
649 DebuggerStartParameters attachParameters = startParameters();
650 attachParameters.executable.clear();
651 attachParameters.processArgs.clear();
652 attachParameters.attachPID = m_consoleStub->applicationPID();
653 attachParameters.startMode = AttachExternal;
654 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
655 QString errorMessage;
656 if (!launchCDB(attachParameters, &errorMessage)) {
657 showMessage(errorMessage, LogError);
658 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
659 notifyEngineSetupFailed();
663 void CdbEngine::consoleStubExited()
667 void CdbEngine::setupEngine()
670 qDebug(">setupEngine");
671 // Nag to add symbol server
672 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
673 &(m_options->symbolPaths)))
674 m_options->toSettings(Core::ICore::instance()->settings());
677 if (!m_logTime.elapsed())
679 QString errorMessage;
680 // Console: Launch the stub with the suspended application and attach to it
681 // CDB in theory has a command line option '-2' that launches a
682 // console, too, but that immediately closes when the debuggee quits.
683 // Use the Creator stub instead.
684 const DebuggerStartParameters &sp = startParameters();
685 const bool launchConsole = isConsole(sp);
686 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
687 const bool ok = launchConsole ?
688 startConsole(startParameters(), &errorMessage) :
689 launchCDB(startParameters(), &errorMessage);
691 qDebug("<setupEngine ok=%d", ok);
693 showMessage(errorMessage, LogError);
694 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
695 notifyEngineSetupFailed();
699 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
702 qDebug("launchCDB startMode=%d", sp.startMode);
703 const QChar blank(QLatin1Char(' '));
704 // Start engine which will run until initial breakpoint:
705 // Determine binary (force MSVC), extension lib name and path to use
706 // The extension is passed as relative name with the path variable set
707 //(does not work with absolute path names)
708 const QString executable = cdbBinary(sp);
709 if (executable.isEmpty()) {
710 *errorMessage = tr("There is no CDB executable specified.");
716 Utils::winIs64BitBinary(executable);
720 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
721 if (!extensionFi.isFile()) {
722 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
723 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
726 const QString extensionFileName = extensionFi.fileName();
728 QStringList arguments;
729 const bool isRemote = sp.startMode == AttachToRemote;
730 if (isRemote) { // Must be first
731 arguments << QLatin1String("-remote") << sp.remoteChannel;
733 arguments << (QLatin1String("-a") + extensionFileName);
735 // Source line info/No terminal breakpoint / Pull extension
736 arguments << QLatin1String("-lines") << QLatin1String("-G")
737 // register idle (debuggee stop) notification
738 << QLatin1String("-c")
739 << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
740 if (sp.useTerminal) // Separate console
741 arguments << QLatin1String("-2");
742 if (!m_options->symbolPaths.isEmpty())
743 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
744 if (!m_options->sourcePaths.isEmpty())
745 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
746 // Compile argument string preserving quotes
747 QString nativeArguments = m_options->additionalArguments;
748 switch (sp.startMode) {
751 if (!nativeArguments.isEmpty())
752 nativeArguments.push_back(blank);
753 nativeArguments += QDir::toNativeSeparators(sp.executable);
758 case AttachCrashedExternal:
759 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
760 if (sp.startMode == AttachCrashedExternal)
761 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
764 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
767 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
768 if (!nativeArguments.isEmpty())
769 nativeArguments.push_back(blank);
770 nativeArguments += sp.processArgs;
773 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
774 arg(QDir::toNativeSeparators(executable),
775 arguments.join(QString(blank)) + blank + nativeArguments,
776 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
777 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
778 showMessage(msg, LogMisc);
780 m_outputBuffer.clear();
781 const QStringList environment = sp.environment.size() == 0 ?
782 QProcessEnvironment::systemEnvironment().toStringList() :
783 sp.environment.toStringList();
784 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
785 if (!sp.workingDirectory.isEmpty())
786 m_process.setWorkingDirectory(sp.workingDirectory);
789 if (!nativeArguments.isEmpty()) // Appends
790 m_process.setNativeArguments(nativeArguments);
792 m_process.start(executable, arguments);
793 if (!m_process.waitForStarted()) {
794 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
795 arg(QDir::toNativeSeparators(executable), m_process.errorString());
799 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
801 const unsigned long pid = 0;
803 showMessage(QString::fromLatin1("%1 running as %2").
804 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
805 m_hasDebuggee = true;
806 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
808 const QByteArray loadCommand = QByteArray(".load ")
809 + extensionFileName.toLocal8Bit();
810 postCommand(loadCommand, 0);
811 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
812 notifyEngineSetupOk();
817 void CdbEngine::setupInferior()
820 qDebug("setupInferior");
821 attemptBreakpointSynchronization();
822 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
823 postCommand(".asm source_line", 0); // Source line in assembly
824 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
827 void CdbEngine::runEngine()
831 // Resume the threads frozen by the console stub.
832 if (isConsole(startParameters()))
833 postCommand("~* m", 0);
834 foreach (const QString &breakEvent, m_options->breakEvents)
835 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
839 bool CdbEngine::commandsPending() const
841 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
844 void CdbEngine::shutdownInferior()
847 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
848 isCdbProcessRunning());
850 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
852 qDebug("notifyInferiorShutdownOk");
853 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
854 notifyInferiorShutdownOk();
859 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
861 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
862 notifyInferiorShutdownOk();
864 // A command got stuck.
865 if (commandsPending()) {
866 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
867 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
868 notifyInferiorShutdownFailed();
871 if (!canInterruptInferior()) {
872 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
873 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
874 notifyInferiorShutdownFailed();
877 interruptInferior(); // Calls us again
881 /* shutdownEngine/processFinished:
882 * Note that in the case of launching a process by the debugger, the debugger
883 * automatically quits a short time after reporting the session becoming
884 * inaccessible without debuggee (notifyInferiorExited). In that case,
885 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
886 * as not to confuse the state engine.
889 void CdbEngine::shutdownEngine()
892 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
893 "accessible=%d,commands pending=%d",
894 stateName(state()), isCdbProcessRunning(), m_accessible,
897 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
898 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
899 notifyEngineShutdownOk();
903 // No longer trigger anything from messages
904 m_ignoreCdbOutput = true;
905 // Go for kill if there are commands pending.
906 if (m_accessible && !commandsPending()) {
907 // detach: Wait for debugger to finish.
908 if (m_effectiveStartMode == AttachExternal)
910 // Remote requires a bit more force to quit.
911 if (m_effectiveStartMode == AttachToRemote) {
912 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
913 postCommand("qq", 0);
917 m_notifyEngineShutdownOnTermination = true;
920 // Remote process. No can do, currently
921 m_notifyEngineShutdownOnTermination = true;
922 Utils::SynchronousProcess::stopProcess(m_process);
925 // Lost debuggee, debugger should quit anytime now
926 if (!m_hasDebuggee) {
927 m_notifyEngineShutdownOnTermination = true;
933 void CdbEngine::processFinished()
936 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
937 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
938 m_process.exitStatus(), m_process.exitCode());
940 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
942 showMessage(tr("CDB crashed"), LogError); // not in your life.
944 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
947 if (m_notifyEngineShutdownOnTermination) {
950 qDebug("notifyEngineIll");
951 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
954 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
955 notifyEngineShutdownOk();
958 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
959 // Otherwise, we take a shortcut.
960 if (isSlaveEngine()) {
961 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
962 notifyInferiorExited();
964 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
965 notifyEngineSpontaneousShutdown();
970 void CdbEngine::detachDebugger()
972 postCommand(".detach", 0);
975 static inline bool isWatchIName(const QByteArray &iname)
977 return iname.startsWith("watch");
980 void CdbEngine::updateWatchData(const WatchData &dataIn,
981 const WatchUpdateFlags & flags)
983 if (debug || debugLocals || debugWatches)
984 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
985 elapsedLogTime(), m_accessible, stateName(state()),
986 flags.tryIncremental,
987 qPrintable(dataIn.toString()));
989 if (!m_accessible) // Add watch data while running?
993 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
995 ByteArrayInputStream str(args);
996 str << dataIn.iname << " \"" << dataIn.exp << '"';
997 postExtensionCommand("addwatch", args, 0,
998 &CdbEngine::handleAddWatch, 0,
999 qVariantFromValue(dataIn));
1003 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
1004 WatchData data = dataIn;
1005 data.setAllUnneeded();
1006 watchHandler()->insertData(data);
1009 updateLocalVariable(dataIn.iname);
1012 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1014 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1016 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1017 if (reply->success) {
1018 updateLocalVariable(item.iname);
1020 item.setError(tr("Unable to add expression"));
1021 watchHandler()->insertData(item);
1022 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1023 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1024 reply->errorMessage), LogError);
1028 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1030 if (debuggerCore()->boolSetting(VerboseLog))
1031 str << blankSeparator << "-v";
1032 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1033 str << blankSeparator << "-c";
1034 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1035 if (!typeFormats.isEmpty())
1036 str << blankSeparator << "-T " << typeFormats;
1037 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1038 if (!individualFormats.isEmpty())
1039 str << blankSeparator << "-I " << individualFormats;
1042 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1044 const bool isWatch = isWatchIName(iname);
1046 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1047 QByteArray localsArguments;
1048 ByteArrayInputStream str(localsArguments);
1049 addLocalsOptions(str);
1051 const int stackFrame = stackHandler()->currentIndex();
1052 if (stackFrame < 0) {
1053 qWarning("Internal error; no stack frame in updateLocalVariable");
1056 str << blankSeparator << stackFrame;
1058 str << blankSeparator << iname;
1059 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1062 unsigned CdbEngine::debuggerCapabilities() const
1064 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1065 |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
1066 |ReloadModuleCapability
1067 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1068 |BreakConditionCapability|TracePointCapability
1069 |BreakModuleCapability;
1072 void CdbEngine::executeStep()
1074 if (!m_operateByInstruction)
1075 m_sourceStepInto = true; // See explanation at handleStackTrace().
1076 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1077 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1078 notifyInferiorRunRequested();
1081 void CdbEngine::executeStepOut()
1083 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1084 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1085 notifyInferiorRunRequested();
1088 void CdbEngine::executeNext()
1090 postCommand(QByteArray("p"), 0); // Step over -> p
1091 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1092 notifyInferiorRunRequested();
1095 void CdbEngine::executeStepI()
1100 void CdbEngine::executeNextI()
1105 void CdbEngine::continueInferior()
1107 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1108 notifyInferiorRunRequested();
1109 doContinueInferior();
1112 void CdbEngine::doContinueInferior()
1114 postCommand(QByteArray("g"), 0);
1117 bool CdbEngine::canInterruptInferior() const
1119 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1122 void CdbEngine::interruptInferior()
1125 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1126 if (canInterruptInferior()) {
1127 doInterruptInferior(NoSpecialStop);
1129 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1130 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1131 notifyInferiorStopOk();
1132 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1133 notifyInferiorRunRequested();
1134 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1135 notifyInferiorRunOk();
1139 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1141 if (m_specialStopMode == NoSpecialStop)
1142 doInterruptInferior(CustomSpecialStop);
1143 m_customSpecialStopData.push_back(v);
1146 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1149 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1150 m_specialStopMode = sm;
1151 QString errorMessage;
1152 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1153 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1154 m_specialStopMode = oldSpecialMode;
1155 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1162 void CdbEngine::executeRunToLine(const ContextData &data)
1164 // Add one-shot breakpoint
1165 BreakpointParameters bp;
1167 bp.type =BreakpointByAddress;
1168 bp.address = data.address;
1170 bp.type =BreakpointByFileAndLine;
1171 bp.fileName = data.fileName;
1172 bp.lineNumber = data.lineNumber;
1174 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1178 void CdbEngine::executeRunToFunction(const QString &functionName)
1180 // Add one-shot breakpoint
1181 BreakpointParameters bp(BreakpointByFunction);
1182 bp.functionName = functionName;
1184 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1188 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1190 const Registers registers = registerHandler()->registers();
1191 QTC_ASSERT(regnr < registers.size(), return)
1192 // Value is decimal or 0x-hex-prefixed
1194 ByteArrayInputStream str(cmd);
1195 str << "r " << registers.at(regnr).name << '=' << value;
1196 postCommand(cmd, 0);
1200 void CdbEngine::executeJumpToLine(const ContextData &data)
1203 // Goto address directly.
1204 jumpToAddress(data.address);
1205 gotoLocation(Location(data.address));
1207 // Jump to source line: Resolve source line address and go to that location
1209 ByteArrayInputStream str(cmd);
1210 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1211 const QVariant cookie = qVariantFromValue(data);
1212 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1216 void CdbEngine::jumpToAddress(quint64 address)
1218 // Fake a jump to address by setting the PC register.
1219 QByteArray registerCmd;
1220 ByteArrayInputStream str(registerCmd);
1221 // PC-register depending on 64/32bit.
1222 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1223 str.setHexPrefix(true);
1224 str.setIntegerBase(16);
1226 postCommand(registerCmd, 0);
1229 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1231 if (cmd->reply.isEmpty())
1233 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1234 // Set register 'rip' to hex address and goto lcoation
1235 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1236 const int equalPos = answer.indexOf(" = ");
1239 answer.remove(0, equalPos + 3);
1240 answer.remove(QLatin1Char('`'));
1242 const quint64 address = answer.toLongLong(&ok, 16);
1243 if (ok && address) {
1244 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1245 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1246 jumpToAddress(address);
1247 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1251 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1254 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1256 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1257 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1262 ByteArrayInputStream str(cmd);
1263 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1264 postCommand(cmd, 0);
1265 // Update all locals in case we change a union or something pointed to
1266 // that affects other variables, too.
1270 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1272 int currentThreadId;
1273 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1274 threadsHandler()->setThreads(threads);
1275 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1276 forceCurrentThreadId : currentThreadId);
1279 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1282 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1283 if (reply->success) {
1285 data.fromString(reply->reply);
1287 // Continue sequence
1288 postCommandSequence(reply->commandSequence);
1290 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1294 void CdbEngine::executeDebuggerCommand(const QString &command)
1296 postCommand(command.toLocal8Bit(), QuietCommand);
1299 // Post command without callback
1300 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1303 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1304 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1305 if (!(flags & QuietCommand))
1306 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1307 m_process.write(cmd + '\n');
1310 // Post a built-in-command producing free-format output with a callback.
1311 // In order to catch the output, it is enclosed in 'echo' commands
1312 // printing a specially formatted token to be identifiable in the output.
1313 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1314 BuiltinCommandHandler handler,
1315 unsigned nextCommandFlag,
1316 const QVariant &cookie)
1318 if (!m_accessible) {
1319 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1320 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1321 showMessage(msg, LogError);
1324 if (!flags & QuietCommand)
1325 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1327 const int token = m_nextCommandToken++;
1328 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1330 m_builtinCommandQueue.push_back(pendingCommand);
1331 // Enclose command in echo-commands for token
1333 ByteArrayInputStream str(fullCmd);
1334 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1335 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1337 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1338 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1339 m_builtinCommandQueue.size(), nextCommandFlag);
1341 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1342 fullCmd.constData());
1343 m_process.write(fullCmd);
1346 // Post an extension command producing one-line output with a callback,
1347 // pass along token for identification in queue.
1348 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1349 const QByteArray &arguments,
1351 ExtensionCommandHandler handler,
1352 unsigned nextCommandFlag,
1353 const QVariant &cookie)
1355 if (!m_accessible) {
1356 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1357 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1358 showMessage(msg, LogError);
1362 const int token = m_nextCommandToken++;
1364 // Format full command with token to be recognizeable in the output
1366 ByteArrayInputStream str(fullCmd);
1367 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1368 if (!arguments.isEmpty())
1369 str << ' ' << arguments;
1371 if (!flags & QuietCommand)
1372 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1374 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1376 m_extensionCommandQueue.push_back(pendingCommand);
1377 // Enclose command in echo-commands for token
1379 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1380 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1381 m_extensionCommandQueue.size(), nextCommandFlag);
1382 m_process.write(fullCmd + '\n');
1385 void CdbEngine::activateFrame(int index)
1387 // TODO: assembler,etc
1390 const StackFrames &frames = stackHandler()->frames();
1391 QTC_ASSERT(index < frames.size(), return; )
1393 const StackFrame frame = frames.at(index);
1394 if (debug || debugLocals)
1395 qDebug("activateFrame idx=%d '%s' %d", index,
1396 qPrintable(frame.file), frame.line);
1397 stackHandler()->setCurrentIndex(index);
1398 const bool showAssembler = !frames.at(index).isUsable();
1399 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1400 watchHandler()->beginCycle();
1401 watchHandler()->endCycle();
1402 QAction *assemblerAction = theAssemblerAction();
1403 if (assemblerAction->isChecked()) {
1404 gotoLocation(frame);
1406 assemblerAction->trigger(); // Seems to trigger update
1409 gotoLocation(frame);
1414 void CdbEngine::updateLocals(bool forNewStackFrame)
1416 typedef QHash<QByteArray, int> WatcherHash;
1418 const int frameIndex = stackHandler()->currentIndex();
1419 if (frameIndex < 0) {
1420 watchHandler()->beginCycle();
1421 watchHandler()->endCycle();
1424 const StackFrame frame = stackHandler()->currentFrame();
1425 if (!frame.isUsable()) {
1426 watchHandler()->beginCycle();
1427 watchHandler()->endCycle();
1430 /* Watchers: Forcibly discard old symbol group as switching from
1431 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1432 * and cause errors as it seems to go 'stale' when switching threads.
1433 * Initial expand, get uninitialized and query */
1434 QByteArray arguments;
1435 ByteArrayInputStream str(arguments);
1438 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1439 if (!expanded.isEmpty()) {
1440 str << blankSeparator << "-e ";
1442 foreach(const QByteArray &e, expanded) {
1448 addLocalsOptions(str);
1449 // Uninitialized variables if desired. Quote as safeguard against shadowed
1450 // variables in case of errors in uninitializedVariables().
1451 if (debuggerCore()->boolSetting(UseCodeModel)) {
1452 QStringList uninitializedVariables;
1453 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1454 frame.function, frame.file, frame.line, &uninitializedVariables);
1455 if (!uninitializedVariables.isEmpty()) {
1456 str << blankSeparator << "-u \"";
1458 foreach(const QString &u, uninitializedVariables) {
1461 str << localsPrefixC << u;
1466 // Perform watches synchronization
1467 str << blankSeparator << "-W";
1468 const WatcherHash watcherHash = WatchHandler::watcherNames();
1469 if (!watcherHash.isEmpty()) {
1470 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1471 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1472 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1476 // Required arguments: frame
1477 str << blankSeparator << frameIndex;
1478 watchHandler()->beginCycle();
1479 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1482 void CdbEngine::selectThread(int index)
1484 if (index < 0 || index == threadsHandler()->currentThread())
1488 const int newThreadId = threadsHandler()->threads().at(index).id;
1489 threadsHandler()->setCurrentThread(index);
1491 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1492 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1495 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1497 QTC_ASSERT(m_accessible, return;)
1499 ByteArrayInputStream str(cmd);
1500 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1501 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1502 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1505 // Parse: "00000000`77606060 cc int 3"
1506 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1508 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1509 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1510 agent->setContents(parseCdbDisassembler(command->reply));
1513 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1516 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1517 const MemoryViewCookie cookie(agent, editor, addr, length);
1519 postFetchMemory(cookie);
1521 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1525 void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
1528 ByteArrayInputStream str(args);
1529 str << cookie.address << ' ' << cookie.length;
1530 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0,
1531 qVariantFromValue(cookie));
1534 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1536 QTC_ASSERT(!data.isEmpty(), return; )
1537 if (!m_accessible) {
1538 const MemoryChangeCookie cookie(addr, data);
1539 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1541 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1545 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1547 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1548 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1549 if (command->success) {
1550 const QByteArray data = QByteArray::fromBase64(command->reply);
1551 if (unsigned(data.size()) == memViewCookie.length)
1552 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1553 memViewCookie.address, data);
1555 showMessage(QString::fromLocal8Bit(command->errorMessage), LogWarning);
1559 void CdbEngine::reloadModules()
1561 postCommandSequence(CommandListModules);
1564 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1568 void CdbEngine::loadAllSymbols()
1572 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1574 Q_UNUSED(moduleName)
1577 void CdbEngine::reloadRegisters()
1579 postCommandSequence(CommandListRegisters);
1582 void CdbEngine::reloadSourceFiles()
1586 void CdbEngine::reloadFullStack()
1589 qDebug("%s", Q_FUNC_INFO);
1590 postCommandSequence(CommandListStack);
1593 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1595 if (reply->success) {
1596 m_inferiorPid = reply->reply.toUInt();
1597 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1598 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1599 notifyInferiorSetupOk();
1601 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1602 arg(QLatin1String(reply->errorMessage)), LogError);
1603 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1604 notifyInferiorSetupFailed();
1608 // Parse CDB gdbmi register syntax
1609 static inline Register parseRegister(const GdbMi &gdbmiReg)
1612 reg.name = gdbmiReg.findChild("name").data();
1613 const GdbMi description = gdbmiReg.findChild("description");
1614 if (description.type() != GdbMi::Invalid) {
1616 reg.name += description.data();
1619 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1623 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1625 if (reply->success) {
1627 value.fromString(reply->reply);
1628 if (value.type() == GdbMi::List) {
1630 modules.reserve(value.childCount());
1631 foreach (const GdbMi &gdbmiModule, value.children()) {
1633 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1634 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1635 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1636 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1637 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1638 module.symbolsRead = Module::ReadOk;
1639 modules.push_back(module);
1641 modulesHandler()->setModules(modules);
1643 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1644 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1647 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1648 arg(QLatin1String(reply->errorMessage)), LogError);
1650 postCommandSequence(reply->commandSequence);
1654 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1656 if (reply->success) {
1658 value.fromString(reply->reply);
1659 if (value.type() == GdbMi::List) {
1660 Registers registers;
1661 registers.reserve(value.childCount());
1662 foreach (const GdbMi &gdbmiReg, value.children())
1663 registers.push_back(parseRegister(gdbmiReg));
1664 registerHandler()->setAndMarkRegisters(registers);
1666 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1667 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1670 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1671 arg(QLatin1String(reply->errorMessage)), LogError);
1673 postCommandSequence(reply->commandSequence);
1676 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1678 if (reply->success) {
1679 QList<WatchData> watchData;
1681 root.fromString(reply->reply);
1682 QTC_ASSERT(root.isList(), return ; )
1684 qDebug() << root.toString(true, 4);
1686 // Courtesy of GDB engine
1687 foreach (const GdbMi &child, root.children()) {
1689 dummy.iname = child.findChild("iname").data();
1690 dummy.name = QLatin1String(child.findChild("name").data());
1691 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1693 watchHandler()->insertBulkData(watchData);
1694 watchHandler()->endCycle();
1696 QDebug nsp = qDebug().nospace();
1697 nsp << "Obtained " << watchData.size() << " items:\n";
1698 foreach (const WatchData &wd, watchData)
1699 nsp << wd.toString() <<'\n';
1701 const bool forNewStackFrame = reply->cookie.toBool();
1702 if (forNewStackFrame)
1703 emit stackFrameCompleted();
1705 showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1709 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1711 if (!reply->success)
1712 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1715 enum CdbExecutionStatus {
1716 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1717 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1718 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1719 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1720 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1721 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1722 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1724 static const char *cdbStatusName(unsigned long s)
1727 case CDB_STATUS_NO_CHANGE:
1731 case CDB_STATUS_GO_HANDLED:
1732 return "go_handled";
1733 case CDB_STATUS_GO_NOT_HANDLED:
1734 return "go_not_handled";
1735 case CDB_STATUS_STEP_OVER:
1737 case CDB_STATUS_STEP_INTO:
1739 case CDB_STATUS_BREAK:
1741 case CDB_STATUS_NO_DEBUGGEE:
1742 return "no_debuggee";
1743 case CDB_STATUS_STEP_BRANCH:
1744 return "step_branch";
1745 case CDB_STATUS_IGNORE_EVENT:
1746 return "ignore_event";
1747 case CDB_STATUS_RESTART_REQUESTED:
1748 return "restart_requested";
1749 case CDB_STATUS_REVERSE_GO:
1750 return "reverse_go";
1751 case CDB_STATUS_REVERSE_STEP_BRANCH:
1752 return "reverse_step_branch";
1753 case CDB_STATUS_REVERSE_STEP_OVER:
1754 return "reverse_step_over";
1755 case CDB_STATUS_REVERSE_STEP_INTO:
1756 return "reverse_step_into";
1761 /* Examine how to react to a stop. */
1762 enum StopActionFlags
1765 StopReportLog = 0x1,
1766 StopReportStatusMessage = 0x2,
1767 StopReportParseError = 0x4,
1768 StopShowExceptionMessageBox = 0x8,
1769 // Notify stop or just continue
1770 StopNotifyStop = 0x10,
1771 StopIgnoreContinue = 0x20,
1772 // Hit on break in artificial stop thread (created by DebugBreak()).
1773 StopInArtificialThread = 0x40,
1774 StopShutdownInProgress = 0x80 // Shutdown in progress
1777 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1778 const QString &threadId)
1780 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1781 .arg(id).arg(number).arg(threadId);
1784 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1785 const QByteArray &condition,
1786 const QString &threadId)
1788 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1789 .arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
1792 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1794 QString *exceptionBoxMessage,
1795 bool conditionalBreakPointTriggered)
1797 // Report stop reason (GDBMI)
1799 if (targetState() == DebuggerFinished)
1800 rc |= StopShutdownInProgress;
1802 qDebug("%s", stopReason.toString(true, 4).constData());
1803 const QByteArray reason = stopReason.findChild("reason").data();
1804 if (reason.isEmpty()) {
1805 *message = tr("Malformed stop response received.");
1806 rc |= StopReportParseError|StopNotifyStop;
1809 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1810 if (state() == InferiorStopOk) {
1811 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1812 arg(QString::fromAscii(reason));
1813 rc |= StopReportLog;
1816 const int threadId = stopReason.findChild("threadId").data().toInt();
1817 if (reason == "breakpoint") {
1818 // Note: Internal breakpoints (run to line) are reported with id=0.
1819 // Step out creates temporary breakpoints with id 10000.
1820 BreakpointId id = 0;
1822 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1823 if (breakpointIdG.isValid()) {
1824 id = breakpointIdG.data().toULongLong();
1825 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1826 const BreakpointResponse parameters = breakHandler()->response(id);
1827 // Trace point? Just report.
1828 number = parameters.number;
1829 if (parameters.tracepoint) {
1830 *message = msgTracePointTriggered(id, number, QString::number(threadId));
1831 return StopReportLog|StopIgnoreContinue;
1833 // Trigger evaluation of BP expression unless we are already in the response.
1834 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1835 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1836 QString::number(threadId));
1837 ConditionalBreakPointCookie cookie(id);
1838 cookie.stopReason = stopReason;
1839 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1840 return StopReportLog;
1846 if (id && breakHandler()->type(id) == Watchpoint) {
1847 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1849 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1851 rc |= StopReportStatusMessage|StopNotifyStop;
1854 if (reason == "exception") {
1855 WinException exception;
1856 exception.fromGdbMI(stopReason);
1857 QString description = exception.toString();
1859 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1860 // pulls DLLs. Avoid showing a 'stopped' Message box.
1861 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1862 || exception.exceptionCode == winExceptionWX86Breakpoint)
1863 return StopNotifyStop;
1864 if (exception.exceptionCode == winExceptionCtrlPressed) {
1865 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1866 *message = msgInterrupted();
1867 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1870 if (isDebuggerWinException(exception.exceptionCode)) {
1871 rc |= StopReportStatusMessage|StopNotifyStop;
1872 // Detect interruption by DebugBreak() and force a switch to thread 0.
1873 if (exception.function == "ntdll!DbgBreakPoint")
1874 rc |= StopInArtificialThread;
1875 *message = msgInterrupted();
1879 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1880 *message = description;
1881 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1884 *message = msgStopped(QLatin1String(reason));
1885 rc |= StopReportStatusMessage|StopNotifyStop;
1889 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1895 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1896 elapsedLogTime(), messageBA.constData(),
1897 stateName(state()), m_specialStopMode);
1899 // Switch source level debugging
1900 syncOperateByInstruction(m_operateByInstructionPending);
1902 // Engine-special stop reasons: Breakpoints and setup
1903 const SpecialStopMode specialStopMode = m_specialStopMode;
1905 m_specialStopMode = NoSpecialStop;
1907 switch(specialStopMode) {
1908 case SpecialStopSynchronizeBreakpoints:
1910 qDebug("attemptBreakpointSynchronization in special stop");
1911 attemptBreakpointSynchronization();
1912 doContinueInferior();
1914 case SpecialStopGetWidgetAt:
1915 postWidgetAtCommand();
1917 case CustomSpecialStop:
1918 foreach (const QVariant &data, m_customSpecialStopData)
1919 handleCustomSpecialStop(data);
1920 m_customSpecialStopData.clear();
1921 doContinueInferior();
1927 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1928 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1929 notifyEngineSetupOk();
1933 stopReason.fromString(messageBA);
1934 processStop(stopReason, false);
1937 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1939 // Further examine stop and report to user
1941 QString exceptionBoxMessage;
1942 int forcedThreadId = -1;
1943 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1944 conditionalBreakPointTriggered);
1945 // Do the non-blocking log reporting
1946 if (stopFlags & StopReportLog)
1947 showMessage(message, LogMisc);
1948 if (stopFlags & StopReportStatusMessage)
1949 showStatusMessage(message);
1950 if (stopFlags & StopReportParseError)
1951 showMessage(message, LogError);
1952 // Ignore things like WOW64, report tracepoints.
1953 if (stopFlags & StopIgnoreContinue) {
1954 postCommand("g", 0);
1957 // Notify about state and send off command sequence to get stack, etc.
1958 if (stopFlags & StopNotifyStop) {
1959 if (state() == InferiorStopRequested) {
1960 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1961 notifyInferiorStopOk();
1963 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1964 notifyInferiorSpontaneousStop();
1966 // Prevent further commands from being sent if shutdown is in progress
1967 if (stopFlags & StopShutdownInProgress) {
1968 showMessage(QString::fromLatin1("Shutdown request detected..."));
1971 const bool sourceStepInto = m_sourceStepInto;
1972 m_sourceStepInto = false;
1973 // Start sequence to get all relevant data.
1974 if (stopFlags & StopInArtificialThread) {
1975 showMessage(tr("Switching to main thread..."), LogMisc);
1976 postCommand("~0 s", 0);
1978 // Re-fetch stack again.
1979 postCommandSequence(CommandListStack);
1981 const GdbMi stack = stopReason.findChild("stack");
1982 if (stack.isValid()) {
1983 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1984 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1988 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1991 const GdbMi threads = stopReason.findChild("threads");
1992 if (threads.isValid()) {
1993 parseThreads(threads, forcedThreadId);
1995 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1997 // Fire off remaining commands asynchronously
1998 if (!m_pendingBreakpointMap.isEmpty())
1999 postCommandSequence(CommandListBreakPoints);
2000 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
2001 postCommandSequence(CommandListRegisters);
2002 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2003 postCommandSequence(CommandListModules);
2005 // After the sequence has been sent off and CDB is pondering the commands,
2006 // pop up a message box for exceptions.
2007 if (stopFlags & StopShowExceptionMessageBox)
2008 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2011 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2013 const DebuggerState s = state();
2014 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2018 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2019 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2022 case EngineShutdownRequested:
2025 case InferiorShutdownRequested:
2033 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2035 const DebuggerState s = state();
2038 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2042 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2043 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2046 case EngineSetupRequested:
2048 case EngineRunRequested:
2049 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2050 notifyEngineRunAndInferiorRunOk();
2053 case InferiorStopOk:
2054 // Inaccessible without debuggee (exit breakpoint)
2055 // We go for spontaneous engine shutdown instead.
2056 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2058 qDebug("Lost debuggeee");
2059 m_hasDebuggee = false;
2062 case InferiorRunRequested:
2063 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2064 notifyInferiorRunOk();
2067 case EngineShutdownRequested:
2074 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2077 QDebug nospace = qDebug().nospace();
2078 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2079 << ' ' << stateName(state());
2080 if (t == 'N' || debug > 1) {
2081 nospace << ' ' << message;
2083 nospace << ' ' << message.size() << " bytes";
2087 // Is there a reply expected, some command queued?
2088 if (t == 'R' || t == 'N') {
2089 if (token == -1) { // Default token, user typed in extension command
2090 showMessage(QString::fromLatin1(message), LogMisc);
2093 const int index = indexOfCommand(m_extensionCommandQueue, token);
2095 // Did the command finish? Take off queue and complete, invoke CB
2096 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2098 command->success = true;
2099 command->reply = message;
2101 command->success = false;
2102 command->errorMessage = message;
2105 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2106 command->command.constData(), command->token, m_extensionCommandQueue.size());
2107 if (command->handler)
2108 (this->*(command->handler))(command);
2113 if (what == "debuggee_output") {
2114 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2118 if (what == "event") {
2119 showStatusMessage(QString::fromAscii(message), 5000);
2123 if (what == "session_accessible") {
2124 if (!m_accessible) {
2125 m_accessible = true;
2126 handleSessionAccessible(message.toULong());
2131 if (what == "session_inaccessible") {
2133 m_accessible = false;
2134 handleSessionInaccessible(message.toULong());
2139 if (what == "session_idle") {
2140 handleSessionIdle(message);
2144 if (what == "exception") {
2145 WinException exception;
2147 gdbmi.fromString(message);
2148 exception.fromGdbMI(gdbmi);
2149 const QString message = exception.toString(true);
2150 showStatusMessage(message);
2151 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2152 if (exception.exceptionCode == winExceptionCppException)
2153 showMessage(message + QLatin1Char('\n'), AppOutput);
2161 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2162 enum { CdbPromptLength = 7 };
2164 static inline bool isCdbPrompt(const QByteArray &c)
2166 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2167 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2168 && std::isdigit(c.at(4));
2171 // Check for '<token>32>' or '<token>32<'
2172 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2173 int *token, bool *isStart)
2177 const int tokenPrefixSize = tokenPrefix.size();
2178 const int size = c.size();
2179 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2181 switch (c.at(size - 1)) {
2191 if (!c.startsWith(tokenPrefix))
2194 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2198 void CdbEngine::parseOutputLine(QByteArray line)
2200 // The hooked output callback in the extension suppresses prompts,
2201 // it should happen only in initial and exit stages. Note however that
2202 // if the output is not hooked, sequences of prompts are possible which
2203 // can mix things up.
2204 while (isCdbPrompt(line))
2205 line.remove(0, CdbPromptLength);
2206 // An extension notification (potentially consisting of several chunks)
2207 if (line.startsWith(m_creatorExtPrefix)) {
2208 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2209 const char type = line.at(m_creatorExtPrefix.size());
2211 const int tokenPos = m_creatorExtPrefix.size() + 2;
2212 const int tokenEndPos = line.indexOf('|', tokenPos);
2213 QTC_ASSERT(tokenEndPos != -1, return)
2214 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2216 const int remainingChunksPos = tokenEndPos + 1;
2217 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2218 QTC_ASSERT(remainingChunksEndPos != -1, return)
2219 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2220 // const char 'serviceName'
2221 const int whatPos = remainingChunksEndPos + 1;
2222 const int whatEndPos = line.indexOf('|', whatPos);
2223 QTC_ASSERT(whatEndPos != -1, return)
2224 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2225 // Build up buffer, call handler once last chunk was encountered
2226 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2227 if (remainingChunks == 0) {
2228 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2229 m_extensionMessageBuffer.clear();
2233 // Check for command start/end tokens within which the builtin command
2234 // output is enclosed
2236 bool isStartToken = false;
2237 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2239 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2240 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2242 // If there is a current command, wait for end of output indicated by token,
2243 // command, trigger handler and finish, else append to its output.
2244 if (m_currentBuiltinCommandIndex != -1) {
2245 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2246 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2247 if (isCommandToken) {
2248 // Did the command finish? Invoke callback and remove from queue.
2250 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2251 currentCommand->command.constData(), currentCommand->token,
2252 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2253 QTC_ASSERT(token == currentCommand->token, return; );
2254 if (currentCommand->handler)
2255 (this->*(currentCommand->handler))(currentCommand);
2256 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2257 m_currentBuiltinCommandIndex = -1;
2259 // Record output of current command
2260 currentCommand->reply.push_back(line);
2263 } // m_currentCommandIndex
2264 if (isCommandToken) {
2265 // Beginning command token encountered, start to record output.
2266 const int index = indexOfCommand(m_builtinCommandQueue, token);
2267 QTC_ASSERT(isStartToken && index != -1, return; );
2268 m_currentBuiltinCommandIndex = index;
2269 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2271 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2275 showMessage(QString::fromLocal8Bit(line), LogMisc);
2278 void CdbEngine::readyReadStandardOut()
2280 if (m_ignoreCdbOutput)
2282 m_outputBuffer += m_process.readAllStandardOutput();
2283 // Split into lines and parse line by line.
2285 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2286 if (endOfLinePos == -1) {
2290 QByteArray line = m_outputBuffer.left(endOfLinePos);
2291 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2292 line.truncate(line.size() - 1);
2293 parseOutputLine(line);
2294 m_outputBuffer.remove(0, endOfLinePos + 1);
2299 void CdbEngine::readyReadStandardError()
2301 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2304 void CdbEngine::processError()
2306 showMessage(m_process.errorString(), LogError);
2310 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2311 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2313 QByteArray cmd(cmdC);
2314 ByteArrayInputStream str(cmd);
2315 foreach(const BreakpointData *bp, bps)
2316 str << ' ' << bp->bpNumber;
2321 bool CdbEngine::stateAcceptsBreakpointChanges() const
2325 case InferiorStopOk:
2333 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2335 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2336 if (!DebuggerEngine::isCppBreakpoint(data))
2338 switch (data.type) {
2340 case BreakpointAtFork:
2341 //case BreakpointAtVFork:
2342 case BreakpointAtSysCall:
2345 case BreakpointByFileAndLine:
2346 case BreakpointByFunction:
2347 case BreakpointByAddress:
2348 case BreakpointAtThrow:
2349 case BreakpointAtCatch:
2350 case BreakpointAtMain:
2351 case BreakpointAtExec:
2357 void CdbEngine::attemptBreakpointSynchronization()
2360 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2361 // Check if there is anything to be done at all.
2362 BreakHandler *handler = breakHandler();
2363 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2364 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2365 if (acceptsBreakpoint(id))
2366 handler->setEngine(id, this);
2368 // Quick check: is there a need to change something? - Populate module cache
2369 bool changed = false;
2370 const BreakpointIds ids = handler->engineBreakpointIds(this);
2371 foreach (BreakpointId id, ids) {
2372 switch (handler->state(id)) {
2373 case BreakpointInsertRequested:
2374 case BreakpointRemoveRequested:
2375 case BreakpointChangeRequested:
2378 case BreakpointInserted: {
2379 // Collect the new modules matching the files.
2380 // In the future, that information should be obtained from the build system.
2381 const BreakpointParameters &data = handler->breakpointData(id);
2382 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2383 m_fileNameModuleHash.insert(data.fileName, data.module);
2391 if (debugBreakpoints)
2392 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2393 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2397 if (!m_accessible) {
2399 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2400 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2403 // Add/Change breakpoints and store pending ones in map, since
2404 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2405 // handleBreakPoints will the complete that information and set it on the break handler.
2406 bool addedChanged = false;
2407 foreach (BreakpointId id, ids) {
2408 BreakpointParameters parameters = handler->breakpointData(id);
2409 BreakpointResponse response;
2410 response.fromParameters(parameters);
2411 // If we encountered that file and have a module for it: Add it.
2412 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2413 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2414 if (it != m_fileNameModuleHash.constEnd())
2415 parameters.module = it.value();
2417 switch (handler->state(id)) {
2418 case BreakpointInsertRequested:
2419 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2420 if (!parameters.enabled)
2421 postCommand("bd " + QByteArray::number(id), 0);
2422 handler->notifyBreakpointInsertProceeding(id);
2423 handler->notifyBreakpointInsertOk(id);
2424 m_pendingBreakpointMap.insert(id, response);
2425 addedChanged = true;
2426 // Ensure enabled/disabled is correct in handler and line number is there.
2427 handler->setResponse(id, response);
2428 if (debugBreakpoints)
2429 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2431 case BreakpointChangeRequested:
2432 handler->notifyBreakpointChangeProceeding(id);
2433 if (debugBreakpoints)
2434 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2435 qPrintable(parameters.toString()));
2436 if (parameters.enabled != handler->response(id).enabled) {
2437 // Change enabled/disabled breakpoints without triggering update.
2438 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2439 response.pending = false;
2440 response.enabled = parameters.enabled;
2441 handler->setResponse(id, response);
2443 // Delete and re-add, triggering update
2444 addedChanged = true;
2445 postCommand("bc " + QByteArray::number(id), 0);
2446 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2447 m_pendingBreakpointMap.insert(id, response);
2449 handler->notifyBreakpointChangeOk(id);
2451 case BreakpointRemoveRequested:
2452 postCommand("bc " + QByteArray::number(id), 0);
2453 handler->notifyBreakpointRemoveProceeding(id);
2454 handler->notifyBreakpointRemoveOk(id);
2455 m_pendingBreakpointMap.remove(id);
2461 // List breakpoints and send responses
2463 postCommandSequence(CommandListBreakPoints);
2466 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2467 // manager to correctly process it) and convert to clean path.
2468 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2471 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2472 if (it != m_normalizedFileCache.constEnd())
2474 if (debugSourceMapping)
2475 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2476 // Do we have source path mappings? ->Apply.
2477 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2479 // Up/lower case normalization according to Windows.
2481 QString normalized = winNormalizeFileName(fileName);
2483 QString normalized = fileName;
2485 if (debugSourceMapping)
2486 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2487 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2488 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2489 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2491 // At least upper case drive letter if failed.
2492 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2493 result.fileName[0] = result.fileName.at(0).toUpper();
2495 m_normalizedFileCache.insert(f, result);
2496 if (debugSourceMapping)
2497 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2501 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2502 // has more processing.
2503 static StackFrames parseFrames(const GdbMi &gdbmi)
2506 const int count = gdbmi.childCount();
2508 for (int i = 0; i < count; i++) {
2509 const GdbMi &frameMi = gdbmi.childAt(i);
2512 const GdbMi fullName = frameMi.findChild("fullname");
2513 if (fullName.isValid()) {
2514 frame.file = QFile::decodeName(fullName.data());
2515 frame.line = frameMi.findChild("line").data().toInt();
2516 frame.usable = false; // To be decided after source path mapping.
2518 frame.function = QLatin1String(frameMi.findChild("func").data());
2519 frame.from = QLatin1String(frameMi.findChild("from").data());
2520 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2521 rc.push_back(frame);
2526 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2528 // Parse frames, find current. Special handling for step into:
2529 // When stepping into on an actual function (source mode) by executing 't', an assembler
2530 // frame pointing at the jmp instruction is hit (noticeable by top function being
2531 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2532 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2534 StackFrames frames = parseFrames(data);
2535 const int count = frames.size();
2536 for (int i = 0; i < count; i++) {
2537 const bool hasFile = !frames.at(i).file.isEmpty();
2538 // jmp-frame hit by step into, do another 't' and abort sequence.
2539 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2540 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2541 return ParseStackStepInto;
2544 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2545 frames[i].file = fileName.fileName;
2546 frames[i].usable = fileName.exists;
2547 if (current == -1 && frames[i].usable)
2551 if (count && current == -1) // No usable frame, use assembly.
2554 stackHandler()->setFrames(frames);
2555 activateFrame(current);
2559 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2561 if (command->success) {
2563 data.fromString(command->reply);
2564 parseStackTrace(data, false);
2565 postCommandSequence(command->commandSequence);
2567 showMessage(command->errorMessage, LogError);
2571 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2574 if (command->success) {
2575 value = command->reply.toInt();
2577 showMessage(command->errorMessage, LogError);
2579 // Is this a conditional breakpoint?
2580 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2581 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2582 const QString message = value ?
2583 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2584 arg(value).arg(cookie.id) :
2585 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2587 showMessage(message, LogMisc);
2588 // Stop if evaluation is true, else continue
2590 processStop(cookie.stopReason, true);
2592 postCommand("g", 0);
2597 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2599 if (exp.contains(' ') && !exp.startsWith('"')) {
2603 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2606 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2608 postCommandSequence(command->commandSequence);
2611 // Post a sequence of standard commands: Trigger next once one completes successfully
2612 void CdbEngine::postCommandSequence(unsigned mask)
2615 qDebug("postCommandSequence 0x%x\n", mask);
2619 if (mask & CommandListThreads) {
2620 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2623 if (mask & CommandListStack) {
2624 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2627 if (mask & CommandListRegisters) {
2628 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2629 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2632 if (mask & CommandListModules) {
2633 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2636 if (mask & CommandListBreakPoints) {
2637 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2638 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2643 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2645 bool success = false;
2648 if (!reply->success) {
2649 message = QString::fromAscii(reply->errorMessage);
2652 // Should be "namespace::QWidget:0x555"
2653 QString watchExp = QString::fromAscii(reply->reply);
2654 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2656 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2659 // 0x000 -> nothing found
2660 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2661 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2664 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2665 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2666 watchExp.insert(0, QLatin1String("*("));
2667 watchHandler()->watchExpression(watchExp);
2671 showMessage(message, LogWarning);
2672 m_watchPointX = m_watchPointY = 0;
2675 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2678 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2682 str.setIntegerBase(16);
2683 str << ", at 0x" << r.address;
2684 str.setIntegerBase(10);
2687 str << ", disabled";
2688 if (!r.module.isEmpty())
2689 str << ", module: '" << r.module << '\'';
2693 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2695 if (debugBreakpoints)
2696 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2697 if (!reply->success) {
2698 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2702 value.fromString(reply->reply);
2703 if (value.type() != GdbMi::List) {
2704 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2707 handleBreakPoints(value);
2710 void CdbEngine::handleBreakPoints(const GdbMi &value)
2712 // Report all obtained parameters back. Note that not all parameters are reported
2713 // back, so, match by id and complete
2714 if (debugBreakpoints)
2715 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2717 QTextStream str(&message);
2718 BreakHandler *handler = breakHandler();
2719 foreach (const GdbMi &breakPointG, value.children()) {
2720 BreakpointResponse reportedResponse;
2721 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2722 if (debugBreakpoints)
2723 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2724 qPrintable(reportedResponse.toString()));
2726 if (!reportedResponse.pending) {
2727 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2728 if (it != m_pendingBreakpointMap.end()) {
2729 // Complete the response and set on handler.
2730 BreakpointResponse ¤tResponse = it.value();
2731 currentResponse.number = reportedResponse.number;
2732 currentResponse.address = reportedResponse.address;
2733 currentResponse.module = reportedResponse.module;
2734 currentResponse.pending = reportedResponse.pending;
2735 currentResponse.enabled = reportedResponse.enabled;
2736 formatCdbBreakPointResponse(id, currentResponse, str);
2737 if (debugBreakpoints)
2738 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2739 handler->setResponse(id, currentResponse);
2740 m_pendingBreakpointMap.erase(it);
2742 } // not pending reported
2744 if (m_pendingBreakpointMap.empty()) {
2745 str << QLatin1String("All breakpoints have been resolved.\n");
2747 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2749 showMessage(message, LogMisc);
2752 void CdbEngine::watchPoint(const QPoint &p)
2754 m_watchPointX = p.x();
2755 m_watchPointY = p.y();
2757 case InferiorStopOk:
2758 postWidgetAtCommand();
2761 // "Select Widget to Watch" from a running application is currently not
2762 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2763 // but requires some work as not to confuse the engine by state-change notifications
2764 // emitted by the debuggee function call.
2765 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2768 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2769 arg(QString::fromAscii(stateName(state()))), LogWarning);
2774 void CdbEngine::postWidgetAtCommand()
2776 QByteArray arguments = QByteArray::number(m_watchPointX);
2777 arguments.append(' ');
2778 arguments.append(QByteArray::number(m_watchPointY));
2779 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2782 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2784 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2785 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2786 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2789 if (qVariantCanConvert<MemoryViewCookie>(v)) {
2790 postFetchMemory(qVariantValue<MemoryViewCookie>(v));
2795 } // namespace Internal
2796 } // namespace Debugger