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)
1515 if (!m_accessible) {
1516 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1520 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1523 ByteArrayInputStream str(args);
1524 str << addr << ' ' << length;
1525 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1526 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1529 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1531 QTC_ASSERT(!data.isEmpty(), return; )
1532 if (!m_accessible) {
1533 const MemoryChangeCookie cookie(addr, data);
1534 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1536 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1540 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1542 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1543 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1544 if (command->success) {
1545 const QByteArray data = QByteArray::fromBase64(command->reply);
1546 if (unsigned(data.size()) == memViewCookie.length)
1547 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1548 memViewCookie.address, data);
1550 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1554 void CdbEngine::reloadModules()
1556 postCommandSequence(CommandListModules);
1559 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1563 void CdbEngine::loadAllSymbols()
1567 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1569 Q_UNUSED(moduleName)
1572 void CdbEngine::reloadRegisters()
1574 postCommandSequence(CommandListRegisters);
1577 void CdbEngine::reloadSourceFiles()
1581 void CdbEngine::reloadFullStack()
1584 qDebug("%s", Q_FUNC_INFO);
1585 postCommandSequence(CommandListStack);
1588 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1590 if (reply->success) {
1591 m_inferiorPid = reply->reply.toUInt();
1592 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1593 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1594 notifyInferiorSetupOk();
1596 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1597 arg(QLatin1String(reply->errorMessage)), LogError);
1598 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1599 notifyInferiorSetupFailed();
1603 // Parse CDB gdbmi register syntax
1604 static inline Register parseRegister(const GdbMi &gdbmiReg)
1607 reg.name = gdbmiReg.findChild("name").data();
1608 const GdbMi description = gdbmiReg.findChild("description");
1609 if (description.type() != GdbMi::Invalid) {
1611 reg.name += description.data();
1614 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1618 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1620 if (reply->success) {
1622 value.fromString(reply->reply);
1623 if (value.type() == GdbMi::List) {
1625 modules.reserve(value.childCount());
1626 foreach (const GdbMi &gdbmiModule, value.children()) {
1628 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1629 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1630 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1631 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1632 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1633 module.symbolsRead = Module::ReadOk;
1634 modules.push_back(module);
1636 modulesHandler()->setModules(modules);
1638 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1639 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1642 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1643 arg(QLatin1String(reply->errorMessage)), LogError);
1645 postCommandSequence(reply->commandSequence);
1649 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1651 if (reply->success) {
1653 value.fromString(reply->reply);
1654 if (value.type() == GdbMi::List) {
1655 Registers registers;
1656 registers.reserve(value.childCount());
1657 foreach (const GdbMi &gdbmiReg, value.children())
1658 registers.push_back(parseRegister(gdbmiReg));
1659 registerHandler()->setAndMarkRegisters(registers);
1661 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1662 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1665 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1666 arg(QLatin1String(reply->errorMessage)), LogError);
1668 postCommandSequence(reply->commandSequence);
1671 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1673 if (reply->success) {
1674 QList<WatchData> watchData;
1676 root.fromString(reply->reply);
1677 QTC_ASSERT(root.isList(), return ; )
1679 qDebug() << root.toString(true, 4);
1681 // Courtesy of GDB engine
1682 foreach (const GdbMi &child, root.children()) {
1684 dummy.iname = child.findChild("iname").data();
1685 dummy.name = QLatin1String(child.findChild("name").data());
1686 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1688 watchHandler()->insertBulkData(watchData);
1689 watchHandler()->endCycle();
1691 QDebug nsp = qDebug().nospace();
1692 nsp << "Obtained " << watchData.size() << " items:\n";
1693 foreach (const WatchData &wd, watchData)
1694 nsp << wd.toString() <<'\n';
1696 const bool forNewStackFrame = reply->cookie.toBool();
1697 if (forNewStackFrame)
1698 emit stackFrameCompleted();
1700 showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1704 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1706 if (!reply->success)
1707 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1710 enum CdbExecutionStatus {
1711 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1712 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1713 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1714 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1715 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1716 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1717 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1719 static const char *cdbStatusName(unsigned long s)
1722 case CDB_STATUS_NO_CHANGE:
1726 case CDB_STATUS_GO_HANDLED:
1727 return "go_handled";
1728 case CDB_STATUS_GO_NOT_HANDLED:
1729 return "go_not_handled";
1730 case CDB_STATUS_STEP_OVER:
1732 case CDB_STATUS_STEP_INTO:
1734 case CDB_STATUS_BREAK:
1736 case CDB_STATUS_NO_DEBUGGEE:
1737 return "no_debuggee";
1738 case CDB_STATUS_STEP_BRANCH:
1739 return "step_branch";
1740 case CDB_STATUS_IGNORE_EVENT:
1741 return "ignore_event";
1742 case CDB_STATUS_RESTART_REQUESTED:
1743 return "restart_requested";
1744 case CDB_STATUS_REVERSE_GO:
1745 return "reverse_go";
1746 case CDB_STATUS_REVERSE_STEP_BRANCH:
1747 return "reverse_step_branch";
1748 case CDB_STATUS_REVERSE_STEP_OVER:
1749 return "reverse_step_over";
1750 case CDB_STATUS_REVERSE_STEP_INTO:
1751 return "reverse_step_into";
1756 /* Examine how to react to a stop. */
1757 enum StopActionFlags
1760 StopReportLog = 0x1,
1761 StopReportStatusMessage = 0x2,
1762 StopReportParseError = 0x4,
1763 StopShowExceptionMessageBox = 0x8,
1764 // Notify stop or just continue
1765 StopNotifyStop = 0x10,
1766 StopIgnoreContinue = 0x20,
1767 // Hit on break in artificial stop thread (created by DebugBreak()).
1768 StopInArtificialThread = 0x40,
1769 StopShutdownInProgress = 0x80 // Shutdown in progress
1772 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1773 const QString &threadId)
1775 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1776 .arg(id).arg(number).arg(threadId);
1779 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1780 const QByteArray &condition,
1781 const QString &threadId)
1783 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1784 .arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
1787 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1789 QString *exceptionBoxMessage,
1790 bool conditionalBreakPointTriggered)
1792 // Report stop reason (GDBMI)
1794 if (targetState() == DebuggerFinished)
1795 rc |= StopShutdownInProgress;
1797 qDebug("%s", stopReason.toString(true, 4).constData());
1798 const QByteArray reason = stopReason.findChild("reason").data();
1799 if (reason.isEmpty()) {
1800 *message = tr("Malformed stop response received.");
1801 rc |= StopReportParseError|StopNotifyStop;
1804 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1805 if (state() == InferiorStopOk) {
1806 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1807 arg(QString::fromAscii(reason));
1808 rc |= StopReportLog;
1811 const int threadId = stopReason.findChild("threadId").data().toInt();
1812 if (reason == "breakpoint") {
1813 // Note: Internal breakpoints (run to line) are reported with id=0.
1814 // Step out creates temporary breakpoints with id 10000.
1815 BreakpointId id = 0;
1817 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1818 if (breakpointIdG.isValid()) {
1819 id = breakpointIdG.data().toULongLong();
1820 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1821 const BreakpointResponse parameters = breakHandler()->response(id);
1822 // Trace point? Just report.
1823 number = parameters.number;
1824 if (parameters.tracepoint) {
1825 *message = msgTracePointTriggered(id, number, QString::number(threadId));
1826 return StopReportLog|StopIgnoreContinue;
1828 // Trigger evaluation of BP expression unless we are already in the response.
1829 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1830 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1831 QString::number(threadId));
1832 ConditionalBreakPointCookie cookie(id);
1833 cookie.stopReason = stopReason;
1834 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1835 return StopReportLog;
1841 if (id && breakHandler()->type(id) == Watchpoint) {
1842 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1844 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1846 rc |= StopReportStatusMessage|StopNotifyStop;
1849 if (reason == "exception") {
1850 WinException exception;
1851 exception.fromGdbMI(stopReason);
1852 QString description = exception.toString();
1854 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1855 // pulls DLLs. Avoid showing a 'stopped' Message box.
1856 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1857 || exception.exceptionCode == winExceptionWX86Breakpoint)
1858 return StopNotifyStop;
1859 if (exception.exceptionCode == winExceptionCtrlPressed) {
1860 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1861 *message = msgInterrupted();
1862 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1865 if (isDebuggerWinException(exception.exceptionCode)) {
1866 rc |= StopReportStatusMessage|StopNotifyStop;
1867 // Detect interruption by DebugBreak() and force a switch to thread 0.
1868 if (exception.function == "ntdll!DbgBreakPoint")
1869 rc |= StopInArtificialThread;
1870 *message = msgInterrupted();
1874 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1875 *message = description;
1876 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1879 *message = msgStopped(QLatin1String(reason));
1880 rc |= StopReportStatusMessage|StopNotifyStop;
1884 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1890 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1891 elapsedLogTime(), messageBA.constData(),
1892 stateName(state()), m_specialStopMode);
1894 // Switch source level debugging
1895 syncOperateByInstruction(m_operateByInstructionPending);
1897 // Engine-special stop reasons: Breakpoints and setup
1898 const SpecialStopMode specialStopMode = m_specialStopMode;
1900 m_specialStopMode = NoSpecialStop;
1902 switch(specialStopMode) {
1903 case SpecialStopSynchronizeBreakpoints:
1905 qDebug("attemptBreakpointSynchronization in special stop");
1906 attemptBreakpointSynchronization();
1907 doContinueInferior();
1909 case SpecialStopGetWidgetAt:
1910 postWidgetAtCommand();
1912 case CustomSpecialStop:
1913 foreach (const QVariant &data, m_customSpecialStopData)
1914 handleCustomSpecialStop(data);
1915 m_customSpecialStopData.clear();
1916 doContinueInferior();
1922 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1923 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1924 notifyEngineSetupOk();
1928 stopReason.fromString(messageBA);
1929 processStop(stopReason, false);
1932 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1934 // Further examine stop and report to user
1936 QString exceptionBoxMessage;
1937 int forcedThreadId = -1;
1938 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1939 conditionalBreakPointTriggered);
1940 // Do the non-blocking log reporting
1941 if (stopFlags & StopReportLog)
1942 showMessage(message, LogMisc);
1943 if (stopFlags & StopReportStatusMessage)
1944 showStatusMessage(message);
1945 if (stopFlags & StopReportParseError)
1946 showMessage(message, LogError);
1947 // Ignore things like WOW64, report tracepoints.
1948 if (stopFlags & StopIgnoreContinue) {
1949 postCommand("g", 0);
1952 // Notify about state and send off command sequence to get stack, etc.
1953 if (stopFlags & StopNotifyStop) {
1954 if (state() == InferiorStopRequested) {
1955 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1956 notifyInferiorStopOk();
1958 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1959 notifyInferiorSpontaneousStop();
1961 // Prevent further commands from being sent if shutdown is in progress
1962 if (stopFlags & StopShutdownInProgress) {
1963 showMessage(QString::fromLatin1("Shutdown request detected..."));
1966 const bool sourceStepInto = m_sourceStepInto;
1967 m_sourceStepInto = false;
1968 // Start sequence to get all relevant data.
1969 if (stopFlags & StopInArtificialThread) {
1970 showMessage(tr("Switching to main thread..."), LogMisc);
1971 postCommand("~0 s", 0);
1973 // Re-fetch stack again.
1974 postCommandSequence(CommandListStack);
1976 const GdbMi stack = stopReason.findChild("stack");
1977 if (stack.isValid()) {
1978 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1979 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1983 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1986 const GdbMi threads = stopReason.findChild("threads");
1987 if (threads.isValid()) {
1988 parseThreads(threads, forcedThreadId);
1990 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1992 // Fire off remaining commands asynchronously
1993 if (!m_pendingBreakpointMap.isEmpty())
1994 postCommandSequence(CommandListBreakPoints);
1995 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1996 postCommandSequence(CommandListRegisters);
1997 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
1998 postCommandSequence(CommandListModules);
2000 // After the sequence has been sent off and CDB is pondering the commands,
2001 // pop up a message box for exceptions.
2002 if (stopFlags & StopShowExceptionMessageBox)
2003 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2006 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2008 const DebuggerState s = state();
2009 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2013 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2014 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2017 case EngineShutdownRequested:
2020 case InferiorShutdownRequested:
2028 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2030 const DebuggerState s = state();
2033 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2037 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2038 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2041 case EngineSetupRequested:
2043 case EngineRunRequested:
2044 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2045 notifyEngineRunAndInferiorRunOk();
2048 case InferiorStopOk:
2049 // Inaccessible without debuggee (exit breakpoint)
2050 // We go for spontaneous engine shutdown instead.
2051 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2053 qDebug("Lost debuggeee");
2054 m_hasDebuggee = false;
2057 case InferiorRunRequested:
2058 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2059 notifyInferiorRunOk();
2062 case EngineShutdownRequested:
2069 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2072 QDebug nospace = qDebug().nospace();
2073 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2074 << ' ' << stateName(state());
2075 if (t == 'N' || debug > 1) {
2076 nospace << ' ' << message;
2078 nospace << ' ' << message.size() << " bytes";
2082 // Is there a reply expected, some command queued?
2083 if (t == 'R' || t == 'N') {
2084 if (token == -1) { // Default token, user typed in extension command
2085 showMessage(QString::fromLatin1(message), LogMisc);
2088 const int index = indexOfCommand(m_extensionCommandQueue, token);
2090 // Did the command finish? Take off queue and complete, invoke CB
2091 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2093 command->success = true;
2094 command->reply = message;
2096 command->success = false;
2097 command->errorMessage = message;
2100 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2101 command->command.constData(), command->token, m_extensionCommandQueue.size());
2102 if (command->handler)
2103 (this->*(command->handler))(command);
2108 if (what == "debuggee_output") {
2109 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2113 if (what == "event") {
2114 showStatusMessage(QString::fromAscii(message), 5000);
2118 if (what == "session_accessible") {
2119 if (!m_accessible) {
2120 m_accessible = true;
2121 handleSessionAccessible(message.toULong());
2126 if (what == "session_inaccessible") {
2128 m_accessible = false;
2129 handleSessionInaccessible(message.toULong());
2134 if (what == "session_idle") {
2135 handleSessionIdle(message);
2139 if (what == "exception") {
2140 WinException exception;
2142 gdbmi.fromString(message);
2143 exception.fromGdbMI(gdbmi);
2144 const QString message = exception.toString(true);
2145 showStatusMessage(message);
2146 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2147 if (exception.exceptionCode == winExceptionCppException)
2148 showMessage(message + QLatin1Char('\n'), AppOutput);
2156 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2157 enum { CdbPromptLength = 7 };
2159 static inline bool isCdbPrompt(const QByteArray &c)
2161 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2162 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2163 && std::isdigit(c.at(4));
2166 // Check for '<token>32>' or '<token>32<'
2167 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2168 int *token, bool *isStart)
2172 const int tokenPrefixSize = tokenPrefix.size();
2173 const int size = c.size();
2174 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2176 switch (c.at(size - 1)) {
2186 if (!c.startsWith(tokenPrefix))
2189 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2193 void CdbEngine::parseOutputLine(QByteArray line)
2195 // The hooked output callback in the extension suppresses prompts,
2196 // it should happen only in initial and exit stages. Note however that
2197 // if the output is not hooked, sequences of prompts are possible which
2198 // can mix things up.
2199 while (isCdbPrompt(line))
2200 line.remove(0, CdbPromptLength);
2201 // An extension notification (potentially consisting of several chunks)
2202 if (line.startsWith(m_creatorExtPrefix)) {
2203 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2204 const char type = line.at(m_creatorExtPrefix.size());
2206 const int tokenPos = m_creatorExtPrefix.size() + 2;
2207 const int tokenEndPos = line.indexOf('|', tokenPos);
2208 QTC_ASSERT(tokenEndPos != -1, return)
2209 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2211 const int remainingChunksPos = tokenEndPos + 1;
2212 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2213 QTC_ASSERT(remainingChunksEndPos != -1, return)
2214 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2215 // const char 'serviceName'
2216 const int whatPos = remainingChunksEndPos + 1;
2217 const int whatEndPos = line.indexOf('|', whatPos);
2218 QTC_ASSERT(whatEndPos != -1, return)
2219 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2220 // Build up buffer, call handler once last chunk was encountered
2221 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2222 if (remainingChunks == 0) {
2223 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2224 m_extensionMessageBuffer.clear();
2228 // Check for command start/end tokens within which the builtin command
2229 // output is enclosed
2231 bool isStartToken = false;
2232 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2234 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2235 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2237 // If there is a current command, wait for end of output indicated by token,
2238 // command, trigger handler and finish, else append to its output.
2239 if (m_currentBuiltinCommandIndex != -1) {
2240 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2241 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2242 if (isCommandToken) {
2243 // Did the command finish? Invoke callback and remove from queue.
2245 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2246 currentCommand->command.constData(), currentCommand->token,
2247 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2248 QTC_ASSERT(token == currentCommand->token, return; );
2249 if (currentCommand->handler)
2250 (this->*(currentCommand->handler))(currentCommand);
2251 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2252 m_currentBuiltinCommandIndex = -1;
2254 // Record output of current command
2255 currentCommand->reply.push_back(line);
2258 } // m_currentCommandIndex
2259 if (isCommandToken) {
2260 // Beginning command token encountered, start to record output.
2261 const int index = indexOfCommand(m_builtinCommandQueue, token);
2262 QTC_ASSERT(isStartToken && index != -1, return; );
2263 m_currentBuiltinCommandIndex = index;
2264 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2266 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2270 showMessage(QString::fromLocal8Bit(line), LogMisc);
2273 void CdbEngine::readyReadStandardOut()
2275 if (m_ignoreCdbOutput)
2277 m_outputBuffer += m_process.readAllStandardOutput();
2278 // Split into lines and parse line by line.
2280 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2281 if (endOfLinePos == -1) {
2285 QByteArray line = m_outputBuffer.left(endOfLinePos);
2286 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2287 line.truncate(line.size() - 1);
2288 parseOutputLine(line);
2289 m_outputBuffer.remove(0, endOfLinePos + 1);
2294 void CdbEngine::readyReadStandardError()
2296 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2299 void CdbEngine::processError()
2301 showMessage(m_process.errorString(), LogError);
2305 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2306 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2308 QByteArray cmd(cmdC);
2309 ByteArrayInputStream str(cmd);
2310 foreach(const BreakpointData *bp, bps)
2311 str << ' ' << bp->bpNumber;
2316 bool CdbEngine::stateAcceptsBreakpointChanges() const
2320 case InferiorStopOk:
2328 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2330 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2331 if (!DebuggerEngine::isCppBreakpoint(data))
2333 switch (data.type) {
2335 case BreakpointAtFork:
2336 //case BreakpointAtVFork:
2337 case BreakpointAtSysCall:
2340 case BreakpointByFileAndLine:
2341 case BreakpointByFunction:
2342 case BreakpointByAddress:
2343 case BreakpointAtThrow:
2344 case BreakpointAtCatch:
2345 case BreakpointAtMain:
2346 case BreakpointAtExec:
2352 void CdbEngine::attemptBreakpointSynchronization()
2355 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2356 // Check if there is anything to be done at all.
2357 BreakHandler *handler = breakHandler();
2358 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2359 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2360 if (acceptsBreakpoint(id))
2361 handler->setEngine(id, this);
2363 // Quick check: is there a need to change something? - Populate module cache
2364 bool changed = false;
2365 const BreakpointIds ids = handler->engineBreakpointIds(this);
2366 foreach (BreakpointId id, ids) {
2367 switch (handler->state(id)) {
2368 case BreakpointInsertRequested:
2369 case BreakpointRemoveRequested:
2370 case BreakpointChangeRequested:
2373 case BreakpointInserted: {
2374 // Collect the new modules matching the files.
2375 // In the future, that information should be obtained from the build system.
2376 const BreakpointParameters &data = handler->breakpointData(id);
2377 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2378 m_fileNameModuleHash.insert(data.fileName, data.module);
2386 if (debugBreakpoints)
2387 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2388 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2392 if (!m_accessible) {
2394 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2395 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2398 // Add/Change breakpoints and store pending ones in map, since
2399 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2400 // handleBreakPoints will the complete that information and set it on the break handler.
2401 bool addedChanged = false;
2402 foreach (BreakpointId id, ids) {
2403 BreakpointParameters parameters = handler->breakpointData(id);
2404 BreakpointResponse response;
2405 response.fromParameters(parameters);
2406 // If we encountered that file and have a module for it: Add it.
2407 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2408 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2409 if (it != m_fileNameModuleHash.constEnd())
2410 parameters.module = it.value();
2412 switch (handler->state(id)) {
2413 case BreakpointInsertRequested:
2414 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2415 if (!parameters.enabled)
2416 postCommand("bd " + QByteArray::number(id), 0);
2417 handler->notifyBreakpointInsertProceeding(id);
2418 handler->notifyBreakpointInsertOk(id);
2419 m_pendingBreakpointMap.insert(id, response);
2420 addedChanged = true;
2421 // Ensure enabled/disabled is correct in handler and line number is there.
2422 handler->setResponse(id, response);
2423 if (debugBreakpoints)
2424 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2426 case BreakpointChangeRequested:
2427 handler->notifyBreakpointChangeProceeding(id);
2428 if (debugBreakpoints)
2429 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2430 qPrintable(parameters.toString()));
2431 if (parameters.enabled != handler->response(id).enabled) {
2432 // Change enabled/disabled breakpoints without triggering update.
2433 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2434 response.pending = false;
2435 response.enabled = parameters.enabled;
2436 handler->setResponse(id, response);
2438 // Delete and re-add, triggering update
2439 addedChanged = true;
2440 postCommand("bc " + QByteArray::number(id), 0);
2441 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2442 m_pendingBreakpointMap.insert(id, response);
2444 handler->notifyBreakpointChangeOk(id);
2446 case BreakpointRemoveRequested:
2447 postCommand("bc " + QByteArray::number(id), 0);
2448 handler->notifyBreakpointRemoveProceeding(id);
2449 handler->notifyBreakpointRemoveOk(id);
2450 m_pendingBreakpointMap.remove(id);
2456 // List breakpoints and send responses
2458 postCommandSequence(CommandListBreakPoints);
2461 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2462 // manager to correctly process it) and convert to clean path.
2463 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2466 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2467 if (it != m_normalizedFileCache.constEnd())
2469 if (debugSourceMapping)
2470 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2471 // Do we have source path mappings? ->Apply.
2472 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2474 // Up/lower case normalization according to Windows.
2476 QString normalized = winNormalizeFileName(fileName);
2478 QString normalized = fileName;
2480 if (debugSourceMapping)
2481 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2482 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2483 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2484 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2486 // At least upper case drive letter if failed.
2487 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2488 result.fileName[0] = result.fileName.at(0).toUpper();
2490 m_normalizedFileCache.insert(f, result);
2491 if (debugSourceMapping)
2492 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2496 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2497 // has more processing.
2498 static StackFrames parseFrames(const GdbMi &gdbmi)
2501 const int count = gdbmi.childCount();
2503 for (int i = 0; i < count; i++) {
2504 const GdbMi &frameMi = gdbmi.childAt(i);
2507 const GdbMi fullName = frameMi.findChild("fullname");
2508 if (fullName.isValid()) {
2509 frame.file = QFile::decodeName(fullName.data());
2510 frame.line = frameMi.findChild("line").data().toInt();
2511 frame.usable = false; // To be decided after source path mapping.
2513 frame.function = QLatin1String(frameMi.findChild("func").data());
2514 frame.from = QLatin1String(frameMi.findChild("from").data());
2515 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2516 rc.push_back(frame);
2521 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2523 // Parse frames, find current. Special handling for step into:
2524 // When stepping into on an actual function (source mode) by executing 't', an assembler
2525 // frame pointing at the jmp instruction is hit (noticeable by top function being
2526 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2527 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2529 StackFrames frames = parseFrames(data);
2530 const int count = frames.size();
2531 for (int i = 0; i < count; i++) {
2532 const bool hasFile = !frames.at(i).file.isEmpty();
2533 // jmp-frame hit by step into, do another 't' and abort sequence.
2534 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2535 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2536 return ParseStackStepInto;
2539 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2540 frames[i].file = fileName.fileName;
2541 frames[i].usable = fileName.exists;
2542 if (current == -1 && frames[i].usable)
2546 if (count && current == -1) // No usable frame, use assembly.
2549 stackHandler()->setFrames(frames);
2550 activateFrame(current);
2554 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2556 if (command->success) {
2558 data.fromString(command->reply);
2559 parseStackTrace(data, false);
2560 postCommandSequence(command->commandSequence);
2562 showMessage(command->errorMessage, LogError);
2566 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2569 if (command->success) {
2570 value = command->reply.toInt();
2572 showMessage(command->errorMessage, LogError);
2574 // Is this a conditional breakpoint?
2575 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2576 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2577 const QString message = value ?
2578 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2579 arg(value).arg(cookie.id) :
2580 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2582 showMessage(message, LogMisc);
2583 // Stop if evaluation is true, else continue
2585 processStop(cookie.stopReason, true);
2587 postCommand("g", 0);
2592 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2594 if (exp.contains(' ') && !exp.startsWith('"')) {
2598 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2601 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2603 postCommandSequence(command->commandSequence);
2606 // Post a sequence of standard commands: Trigger next once one completes successfully
2607 void CdbEngine::postCommandSequence(unsigned mask)
2610 qDebug("postCommandSequence 0x%x\n", mask);
2614 if (mask & CommandListThreads) {
2615 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2618 if (mask & CommandListStack) {
2619 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2622 if (mask & CommandListRegisters) {
2623 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2624 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2627 if (mask & CommandListModules) {
2628 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2631 if (mask & CommandListBreakPoints) {
2632 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2633 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2638 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2640 bool success = false;
2643 if (!reply->success) {
2644 message = QString::fromAscii(reply->errorMessage);
2647 // Should be "namespace::QWidget:0x555"
2648 QString watchExp = QString::fromAscii(reply->reply);
2649 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2651 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2654 // 0x000 -> nothing found
2655 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2656 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2659 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2660 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2661 watchExp.insert(0, QLatin1String("*("));
2662 watchHandler()->watchExpression(watchExp);
2666 showMessage(message, LogWarning);
2667 m_watchPointX = m_watchPointY = 0;
2670 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2673 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2677 str.setIntegerBase(16);
2678 str << ", at 0x" << r.address;
2679 str.setIntegerBase(10);
2682 str << ", disabled";
2683 if (!r.module.isEmpty())
2684 str << ", module: '" << r.module << '\'';
2688 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2690 if (debugBreakpoints)
2691 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2692 if (!reply->success) {
2693 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2697 value.fromString(reply->reply);
2698 if (value.type() != GdbMi::List) {
2699 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2702 handleBreakPoints(value);
2705 void CdbEngine::handleBreakPoints(const GdbMi &value)
2707 // Report all obtained parameters back. Note that not all parameters are reported
2708 // back, so, match by id and complete
2709 if (debugBreakpoints)
2710 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2712 QTextStream str(&message);
2713 BreakHandler *handler = breakHandler();
2714 foreach (const GdbMi &breakPointG, value.children()) {
2715 BreakpointResponse reportedResponse;
2716 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2717 if (debugBreakpoints)
2718 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2719 qPrintable(reportedResponse.toString()));
2721 if (!reportedResponse.pending) {
2722 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2723 if (it != m_pendingBreakpointMap.end()) {
2724 // Complete the response and set on handler.
2725 BreakpointResponse ¤tResponse = it.value();
2726 currentResponse.number = reportedResponse.number;
2727 currentResponse.address = reportedResponse.address;
2728 currentResponse.module = reportedResponse.module;
2729 currentResponse.pending = reportedResponse.pending;
2730 currentResponse.enabled = reportedResponse.enabled;
2731 formatCdbBreakPointResponse(id, currentResponse, str);
2732 if (debugBreakpoints)
2733 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2734 handler->setResponse(id, currentResponse);
2735 m_pendingBreakpointMap.erase(it);
2737 } // not pending reported
2739 if (m_pendingBreakpointMap.empty()) {
2740 str << QLatin1String("All breakpoints have been resolved.\n");
2742 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2744 showMessage(message, LogMisc);
2747 void CdbEngine::watchPoint(const QPoint &p)
2749 m_watchPointX = p.x();
2750 m_watchPointY = p.y();
2752 case InferiorStopOk:
2753 postWidgetAtCommand();
2756 // "Select Widget to Watch" from a running application is currently not
2757 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2758 // but requires some work as not to confuse the engine by state-change notifications
2759 // emitted by the debuggee function call.
2760 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2763 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2764 arg(QString::fromAscii(stateName(state()))), LogWarning);
2769 void CdbEngine::postWidgetAtCommand()
2771 QByteArray arguments = QByteArray::number(m_watchPointX);
2772 arguments.append(' ');
2773 arguments.append(QByteArray::number(m_watchPointY));
2774 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2777 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2779 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2780 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2781 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2786 } // namespace Internal
2787 } // namespace Debugger