1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "cdbengine.h"
35 #include "debuggerstartparameters.h"
36 #include "disassemblerlines.h"
37 #include "cdboptions.h"
38 #include "cdboptionspage.h"
39 #include "bytearrayinputstream.h"
40 #include "breakpoint.h"
41 #include "breakhandler.h"
42 #include "stackframe.h"
43 #include "stackhandler.h"
44 #include "watchhandler.h"
45 #include "threadshandler.h"
46 #include "moduleshandler.h"
47 #include "debuggeractions.h"
48 #include "debuggercore.h"
49 #include "registerhandler.h"
50 #include "disassembleragent.h"
51 #include "memoryagent.h"
52 #include "debuggerrunner.h"
53 #include "debuggertooltipmanager.h"
54 #include "cdbparsehelpers.h"
55 #include "watchutils.h"
56 #include "gdb/gdbmi.h"
57 #include "shared/cdbsymbolpathlisteditor.h"
59 #include <coreplugin/icore.h>
60 #include <texteditor/itexteditor.h>
61 #include <projectexplorer/abi.h>
62 #include <projectexplorer/projectexplorerconstants.h>
64 #include <utils/synchronousprocess.h>
65 #include <utils/winutils.h>
66 #include <utils/qtcassert.h>
67 #include <utils/savedaction.h>
68 #include <utils/consoleprocess.h>
70 #include <QtCore/QCoreApplication>
71 #include <QtCore/QFileInfo>
72 #include <QtCore/QDir>
73 #include <QtCore/QDebug>
74 #include <QtCore/QTextStream>
75 #include <QtCore/QDateTime>
76 #include <QtGui/QToolTip>
77 #include <QtGui/QMainWindow>
78 #include <QtGui/QMessageBox>
81 # include <utils/winutils.h>
82 # include "dbgwinutils.h"
87 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
88 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
91 enum { debugLocals = 0 };
92 enum { debugSourceMapping = 0 };
93 enum { debugWatches = 0 };
94 enum { debugBreakpoints = 0 };
97 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
99 # define STATE_DEBUG(state, func, line, notifyFunc)
103 \class Debugger::Internal::CdbEngine
105 Cdb engine version 2: Run the CDB process on pipes and parse its output.
106 The engine relies on a CDB extension Qt Creator provides as an extension
107 library (32/64bit), which is loaded into cdb.exe. It serves to:
110 \o Notify the engine about the state of the debugging session:
112 \o idle: (hooked up with .idle_cmd) debuggee stopped
113 \o accessible: Debuggee stopped, cdb.exe accepts commands
114 \o inaccessible: Debuggee runs, no way to post commands
115 \o session active/inactive: Lost debuggee, terminating.
117 \o Hook up with output/event callbacks and produce formatted output to be able
118 to catch application output and exceptions.
119 \o Provide some extension commands that produce output in a standardized (GDBMI)
120 format that ends up in handleExtensionMessage(), for example:
122 \o pid Return debuggee pid for interrupting.
123 \o locals Print locals from SymbolGroup
124 \o expandLocals Expand locals in symbol group
125 \o registers, modules, threads
129 Debugger commands can be posted by calling:
133 \o postCommand(): Does not expect a reply
134 \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
135 that is captured by enclosing it in special tokens using the 'echo' command and
136 then invokes a callback with a CdbBuiltinCommand structure.
137 \o postExtensionCommand(): Run a command provided by the extension producing
138 one-line output and invoke a callback with a CdbExtensionCommand structure
139 (output is potentially split up in chunks).
144 [Console: The console stub launches the process. On process startup,
145 launchCDB() is called with AttachExternal].
146 setupEngine() calls launchCDB() with the startparameters. The debuggee
147 runs into the initial breakpoint (session idle). EngineSetupOk is
148 notified (inferior still stopped). setupInferior() is then called
149 which does breakpoint synchronization and issues the extension 'pid'
150 command to obtain the inferior pid (which also hooks up the output callbacks).
151 handlePid() notifies notifyInferiorSetupOk.
152 runEngine() is then called which issues 'g' to continue the inferior.
153 Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
154 when the inferior exits (except attach modes).
157 using namespace ProjectExplorer;
162 static const char localsPrefixC[] = "local.";
164 struct MemoryViewCookie
166 explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
167 quint64 addr = 0, quint64 l = 0) :
168 agent(a), editorToken(e), address(addr), length(l)
172 QObject *editorToken;
177 struct MemoryChangeCookie
179 explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
180 address(addr), data(d) {}
186 struct ConditionalBreakPointCookie
188 ConditionalBreakPointCookie(BreakpointId i = 0) : id(i) {}
193 } // namespace Internal
194 } // namespace Debugger
196 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
197 Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
198 Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
203 static inline bool isConsole(const DebuggerStartParameters &sp)
205 return (sp.startMode == StartInternal || sp.startMode == StartExternal)
210 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
212 QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
213 debuggerCore()->mainWindow());
214 mb->setAttribute(Qt::WA_DeleteOnClose);
219 // Base data structure for command queue entries with callback
220 struct CdbCommandBase
222 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
225 CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
226 unsigned nc, const QVariant &cookie);
232 // Continue with another commands as specified in CommandSequenceFlags
233 unsigned commandSequence;
236 CdbCommandBase::CdbCommandBase() :
237 token(0), flags(0), commandSequence(0)
241 CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
242 unsigned nc, const QVariant &c) :
243 token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
247 // Queue entry for builtin commands producing free-format
248 // line-by-line output.
249 struct CdbBuiltinCommand : public CdbCommandBase
251 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
253 CdbBuiltinCommand() {}
254 CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
256 unsigned nc, const QVariant &cookie) :
257 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
261 QByteArray joinedReply() const;
263 CommandHandler handler;
264 QList<QByteArray> reply;
267 QByteArray CdbBuiltinCommand::joinedReply() const
272 answer.reserve(120 * reply.size());
273 foreach (const QByteArray &l, reply) {
280 // Queue entry for Qt Creator extension commands producing one-line
281 // output with success flag and error message.
282 struct CdbExtensionCommand : public CdbCommandBase
284 typedef CdbEngine::ExtensionCommandHandler CommandHandler;
286 CdbExtensionCommand() : success(false) {}
287 CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
289 unsigned nc, const QVariant &cookie) :
290 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
292 CommandHandler handler;
294 QByteArray errorMessage;
298 template <class CommandPtrType>
299 int indexOfCommand(const QList<CommandPtrType> &l, int token)
301 const int count = l.size();
302 for (int i = 0; i < count; i++)
303 if (l.at(i)->token == token)
308 static inline bool validMode(DebuggerStartMode sm)
322 // Accessed by RunControlFactory
323 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
324 DebuggerEngine *masterEngine, QString *errorMessage)
327 CdbOptionsPage *op = CdbOptionsPage::instance();
328 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
329 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
332 return new CdbEngine(sp, masterEngine, op->options());
334 Q_UNUSED(masterEngine)
337 *errorMessage = QString::fromLatin1("Unsupported debug mode");
341 bool isCdbEngineEnabled()
344 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
350 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
352 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
355 static QString cdbBinary(const DebuggerStartParameters &sp)
357 if (!sp.debuggerCommand.isEmpty()) {
358 // Do not use a GDB binary if we got started for a project with MinGW runtime.
359 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
360 && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
361 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
362 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
364 return sp.debuggerCommand;
366 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
369 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
372 if (!isCdbEngineEnabled()) {
373 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
374 arg(sp.toolChainAbi.toString()));
375 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
376 check->settingsPage = CdbOptionsPage::settingsId();
380 if (!validMode(sp.startMode)) {
381 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
385 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
386 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
387 arg(sp.toolChainAbi.toString()));
391 if (cdbBinary(sp).isEmpty()) {
392 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
393 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
394 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
401 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
406 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
409 opts->push_back(new CdbOptionsPage);
415 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
417 static inline Utils::SavedAction *theAssemblerAction()
419 return debuggerCore()->action(OperateByInstruction);
422 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
423 DebuggerEngine *masterEngine, const OptionsPtr &options) :
424 DebuggerEngine(sp, masterEngine),
425 m_creatorExtPrefix("<qtcreatorcdbext>|"),
426 m_tokenPrefix("<token>"),
428 m_effectiveStartMode(NoStartMode),
431 m_specialStopMode(NoSpecialStop),
432 m_nextCommandToken(0),
433 m_currentBuiltinCommandIndex(-1),
434 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
435 m_operateByInstructionPending(true),
436 m_operateByInstruction(true), // Default CDB setting
437 m_notifyEngineShutdownOnTermination(false),
438 m_hasDebuggee(false),
440 m_sourceStepInto(false),
443 m_ignoreCdbOutput(false)
445 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
447 setObjectName(QLatin1String("CdbEngine"));
448 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
449 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
450 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
451 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
454 void CdbEngine::init()
456 m_effectiveStartMode = NoStartMode;
458 m_accessible = false;
459 m_specialStopMode = NoSpecialStop;
460 m_nextCommandToken = 0;
461 m_currentBuiltinCommandIndex = -1;
462 m_operateByInstructionPending = theAssemblerAction()->isChecked();
463 m_operateByInstruction = true; // Default CDB setting
464 m_notifyEngineShutdownOnTermination = false;
465 m_hasDebuggee = false;
466 m_sourceStepInto = false;
467 m_watchPointX = m_watchPointY = 0;
468 m_ignoreCdbOutput = false;
470 m_outputBuffer.clear();
471 m_builtinCommandQueue.clear();
472 m_extensionCommandQueue.clear();
473 m_extensionMessageBuffer.clear();
474 m_pendingBreakpointMap.clear();
475 m_customSpecialStopData.clear();
477 // Create local list of mappings in native separators
478 m_sourcePathMappings.clear();
479 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
480 if (!globalOptions->sourcePathMap.isEmpty()) {
481 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
482 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
483 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
484 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
485 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
486 QDir::toNativeSeparators(it.value())));
489 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
492 CdbEngine::~CdbEngine()
496 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
498 if (state() == InferiorStopOk) {
499 syncOperateByInstruction(operateByInstruction);
501 // To be set next time session becomes accessible
502 m_operateByInstructionPending = operateByInstruction;
506 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
509 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
510 if (m_operateByInstruction == operateByInstruction)
512 QTC_ASSERT(m_accessible, return; )
513 m_operateByInstruction = operateByInstruction;
514 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
515 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
518 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
519 TextEditor::ITextEditor *editor,
520 const DebuggerToolTipContext &contextIn)
523 qDebug() << Q_FUNC_INFO;
524 // Need a stopped debuggee and a cpp file in a valid frame
525 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
527 // Determine expression and function
530 DebuggerToolTipContext context = contextIn;
531 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
532 // Are we in the current stack frame
533 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
535 // No numerical or any other expressions [yet]
536 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
538 // Can this be found as a local variable?
539 const QByteArray localsPrefix(localsPrefixC);
540 QByteArray iname = localsPrefix + exp.toAscii();
541 QModelIndex index = watchHandler()->itemIndex(iname);
542 if (!index.isValid()) {
543 // Nope, try a 'local.this.m_foo'.
544 exp.prepend(QLatin1String("this."));
545 iname.insert(localsPrefix.size(), "this.");
546 index = watchHandler()->itemIndex(iname);
547 if (!index.isValid())
550 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
551 tw->setContext(context);
552 tw->setDebuggerModel(LocalsWatch);
553 tw->setExpression(exp);
554 tw->acquireEngine(this);
555 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
559 // Determine full path to the CDB extension library.
560 QString CdbEngine::extensionLibraryName(bool is64Bit)
562 // Determine extension lib name and path to use
564 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
565 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
566 << '/' << QT_CREATOR_CDB_EXT << ".dll";
570 // Determine environment for CDB.exe, start out with run config and
571 // add CDB extension path merged with system value should there be one.
572 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
573 QString cdbExtensionPath)
575 // Determine CDB extension path from Qt Creator
576 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
577 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
578 if (!oldCdbExtensionPath.isEmpty()) {
579 cdbExtensionPath.append(QLatin1Char(';'));
580 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
582 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
583 // config, just to make sure, delete any existing entries
584 const QString cdbExtensionPathVariableAssign =
585 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
586 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
587 if (it->startsWith(cdbExtensionPathVariableAssign)) {
588 it = runConfigEnvironment.erase(it);
594 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
595 QDir::toNativeSeparators(cdbExtensionPath));
596 return runConfigEnvironment;
599 int CdbEngine::elapsedLogTime() const
601 const int elapsed = m_logTime.elapsed();
602 const int delta = elapsed - m_elapsedLogTime;
603 m_elapsedLogTime = elapsed;
607 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
608 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
611 qDebug("startConsole %s", qPrintable(sp.executable));
612 m_consoleStub.reset(new Utils::ConsoleProcess);
613 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
614 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
615 SLOT(consoleStubMessage(QString, bool)));
616 connect(m_consoleStub.data(), SIGNAL(processStarted()),
617 SLOT(consoleStubProcessStarted()));
618 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
619 SLOT(consoleStubExited()));
620 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
621 if (sp.environment.size())
622 m_consoleStub->setEnvironment(sp.environment);
623 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
624 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
630 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
633 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
635 if (state() == EngineSetupRequested) {
636 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
637 notifyEngineSetupFailed();
639 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
642 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
644 showMessage(msg, AppOutput);
648 void CdbEngine::consoleStubProcessStarted()
651 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
652 // Attach to console process.
653 DebuggerStartParameters attachParameters = startParameters();
654 attachParameters.executable.clear();
655 attachParameters.processArgs.clear();
656 attachParameters.attachPID = m_consoleStub->applicationPID();
657 attachParameters.startMode = AttachExternal;
658 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
659 QString errorMessage;
660 if (!launchCDB(attachParameters, &errorMessage)) {
661 showMessage(errorMessage, LogError);
662 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
663 notifyEngineSetupFailed();
667 void CdbEngine::consoleStubExited()
671 void CdbEngine::setupEngine()
674 qDebug(">setupEngine");
675 // Nag to add symbol server
676 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
677 &(m_options->symbolPaths)))
678 m_options->toSettings(Core::ICore::instance()->settings());
681 if (!m_logTime.elapsed())
683 QString errorMessage;
684 // Console: Launch the stub with the suspended application and attach to it
685 // CDB in theory has a command line option '-2' that launches a
686 // console, too, but that immediately closes when the debuggee quits.
687 // Use the Creator stub instead.
688 const DebuggerStartParameters &sp = startParameters();
689 const bool launchConsole = isConsole(sp);
690 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
691 const bool ok = launchConsole ?
692 startConsole(startParameters(), &errorMessage) :
693 launchCDB(startParameters(), &errorMessage);
695 qDebug("<setupEngine ok=%d", ok);
697 showMessage(errorMessage, LogError);
698 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
699 notifyEngineSetupFailed();
703 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
706 qDebug("launchCDB startMode=%d", sp.startMode);
707 const QChar blank(QLatin1Char(' '));
708 // Start engine which will run until initial breakpoint:
709 // Determine binary (force MSVC), extension lib name and path to use
710 // The extension is passed as relative name with the path variable set
711 //(does not work with absolute path names)
712 const QString executable = cdbBinary(sp);
713 if (executable.isEmpty()) {
714 *errorMessage = tr("There is no CDB executable specified.");
720 Utils::winIs64BitBinary(executable);
724 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
725 if (!extensionFi.isFile()) {
726 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
727 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
730 const QString extensionFileName = extensionFi.fileName();
732 QStringList arguments;
733 const bool isRemote = sp.startMode == AttachToRemote;
734 if (isRemote) { // Must be first
735 arguments << QLatin1String("-remote") << sp.remoteChannel;
737 arguments << (QLatin1String("-a") + extensionFileName);
739 // Source line info/No terminal breakpoint / Pull extension
740 arguments << QLatin1String("-lines") << QLatin1String("-G")
741 // register idle (debuggee stop) notification
742 << QLatin1String("-c")
743 << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
744 if (sp.useTerminal) // Separate console
745 arguments << QLatin1String("-2");
746 if (!m_options->symbolPaths.isEmpty())
747 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
748 if (!m_options->sourcePaths.isEmpty())
749 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
750 // Compile argument string preserving quotes
751 QString nativeArguments = m_options->additionalArguments;
752 switch (sp.startMode) {
755 if (!nativeArguments.isEmpty())
756 nativeArguments.push_back(blank);
757 nativeArguments += QDir::toNativeSeparators(sp.executable);
762 case AttachCrashedExternal:
763 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
764 if (sp.startMode == AttachCrashedExternal)
765 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
768 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
771 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
772 if (!nativeArguments.isEmpty())
773 nativeArguments.push_back(blank);
774 nativeArguments += sp.processArgs;
777 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
778 arg(QDir::toNativeSeparators(executable),
779 arguments.join(QString(blank)) + blank + nativeArguments,
780 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
781 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
782 showMessage(msg, LogMisc);
784 m_outputBuffer.clear();
785 const QStringList environment = sp.environment.size() == 0 ?
786 QProcessEnvironment::systemEnvironment().toStringList() :
787 sp.environment.toStringList();
788 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
789 if (!sp.workingDirectory.isEmpty())
790 m_process.setWorkingDirectory(sp.workingDirectory);
793 if (!nativeArguments.isEmpty()) // Appends
794 m_process.setNativeArguments(nativeArguments);
796 m_process.start(executable, arguments);
797 if (!m_process.waitForStarted()) {
798 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
799 arg(QDir::toNativeSeparators(executable), m_process.errorString());
803 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
805 const unsigned long pid = 0;
807 showMessage(QString::fromLatin1("%1 running as %2").
808 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
809 m_hasDebuggee = true;
810 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
812 const QByteArray loadCommand = QByteArray(".load ")
813 + extensionFileName.toLocal8Bit();
814 postCommand(loadCommand, 0);
815 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
816 notifyEngineSetupOk();
821 void CdbEngine::setupInferior()
824 qDebug("setupInferior");
825 attemptBreakpointSynchronization();
826 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
827 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
830 void CdbEngine::runEngine()
834 // Resume the threads frozen by the console stub.
835 if (isConsole(startParameters()))
836 postCommand("~* m", 0);
837 foreach (const QString &breakEvent, m_options->breakEvents)
838 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
842 bool CdbEngine::commandsPending() const
844 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
847 void CdbEngine::shutdownInferior()
850 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
851 isCdbProcessRunning());
853 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
855 qDebug("notifyInferiorShutdownOk");
856 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
857 notifyInferiorShutdownOk();
862 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
864 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
865 notifyInferiorShutdownOk();
867 // A command got stuck.
868 if (commandsPending()) {
869 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
870 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
871 notifyInferiorShutdownFailed();
874 if (!canInterruptInferior()) {
875 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
876 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
877 notifyInferiorShutdownFailed();
880 interruptInferior(); // Calls us again
884 /* shutdownEngine/processFinished:
885 * Note that in the case of launching a process by the debugger, the debugger
886 * automatically quits a short time after reporting the session becoming
887 * inaccessible without debuggee (notifyInferiorExited). In that case,
888 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
889 * as not to confuse the state engine.
892 void CdbEngine::shutdownEngine()
895 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
896 "accessible=%d,commands pending=%d",
897 stateName(state()), isCdbProcessRunning(), m_accessible,
900 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
901 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
902 notifyEngineShutdownOk();
906 // No longer trigger anything from messages
907 m_ignoreCdbOutput = true;
908 // Go for kill if there are commands pending.
909 if (m_accessible && !commandsPending()) {
910 // detach: Wait for debugger to finish.
911 if (m_effectiveStartMode == AttachExternal)
913 // Remote requires a bit more force to quit.
914 if (m_effectiveStartMode == AttachToRemote) {
915 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
916 postCommand("qq", 0);
920 m_notifyEngineShutdownOnTermination = true;
923 // Remote process. No can do, currently
924 m_notifyEngineShutdownOnTermination = true;
925 Utils::SynchronousProcess::stopProcess(m_process);
928 // Lost debuggee, debugger should quit anytime now
929 if (!m_hasDebuggee) {
930 m_notifyEngineShutdownOnTermination = true;
936 void CdbEngine::processFinished()
939 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
940 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
941 m_process.exitStatus(), m_process.exitCode());
943 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
945 showMessage(tr("CDB crashed"), LogError); // not in your life.
947 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
950 if (m_notifyEngineShutdownOnTermination) {
953 qDebug("notifyEngineIll");
954 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
957 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
958 notifyEngineShutdownOk();
961 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
962 // Otherwise, we take a shortcut.
963 if (isSlaveEngine()) {
964 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
965 notifyInferiorExited();
967 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
968 notifyEngineSpontaneousShutdown();
973 void CdbEngine::detachDebugger()
975 postCommand(".detach", 0);
978 static inline bool isWatchIName(const QByteArray &iname)
980 return iname.startsWith("watch");
983 void CdbEngine::updateWatchData(const WatchData &dataIn,
984 const WatchUpdateFlags & flags)
986 if (debug || debugLocals || debugWatches)
987 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
988 elapsedLogTime(), m_accessible, stateName(state()),
989 flags.tryIncremental,
990 qPrintable(dataIn.toString()));
992 if (!m_accessible) // Add watch data while running?
996 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
998 ByteArrayInputStream str(args);
999 str << dataIn.iname << " \"" << dataIn.exp << '"';
1000 postExtensionCommand("addwatch", args, 0,
1001 &CdbEngine::handleAddWatch, 0,
1002 qVariantFromValue(dataIn));
1006 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
1007 WatchData data = dataIn;
1008 data.setAllUnneeded();
1009 watchHandler()->insertData(data);
1012 updateLocalVariable(dataIn.iname);
1015 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1017 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1019 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1020 if (reply->success) {
1021 updateLocalVariable(item.iname);
1023 item.setError(tr("Unable to add expression"));
1024 watchHandler()->insertData(item);
1025 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1026 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1027 reply->errorMessage), LogError);
1031 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1033 if (debuggerCore()->boolSetting(VerboseLog))
1034 str << blankSeparator << "-v";
1035 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1036 str << blankSeparator << "-c";
1037 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1038 if (!typeFormats.isEmpty())
1039 str << blankSeparator << "-T " << typeFormats;
1040 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1041 if (!individualFormats.isEmpty())
1042 str << blankSeparator << "-I " << individualFormats;
1045 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1047 const bool isWatch = isWatchIName(iname);
1049 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1050 QByteArray localsArguments;
1051 ByteArrayInputStream str(localsArguments);
1052 addLocalsOptions(str);
1054 const int stackFrame = stackHandler()->currentIndex();
1055 if (stackFrame < 0) {
1056 qWarning("Internal error; no stack frame in updateLocalVariable");
1059 str << blankSeparator << stackFrame;
1061 str << blankSeparator << iname;
1062 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1065 unsigned CdbEngine::debuggerCapabilities() const
1067 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1068 |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
1069 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1070 |BreakConditionCapability|TracePointCapability
1071 |BreakModuleCapability;
1074 void CdbEngine::executeStep()
1076 if (!m_operateByInstruction)
1077 m_sourceStepInto = true; // See explanation at handleStackTrace().
1078 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1079 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1080 notifyInferiorRunRequested();
1083 void CdbEngine::executeStepOut()
1085 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1086 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1087 notifyInferiorRunRequested();
1090 void CdbEngine::executeNext()
1092 postCommand(QByteArray("p"), 0); // Step over -> p
1093 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1094 notifyInferiorRunRequested();
1097 void CdbEngine::executeStepI()
1102 void CdbEngine::executeNextI()
1107 void CdbEngine::continueInferior()
1109 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1110 notifyInferiorRunRequested();
1111 doContinueInferior();
1114 void CdbEngine::doContinueInferior()
1116 postCommand(QByteArray("g"), 0);
1119 bool CdbEngine::canInterruptInferior() const
1121 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1124 void CdbEngine::interruptInferior()
1127 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1128 if (canInterruptInferior()) {
1129 doInterruptInferior(NoSpecialStop);
1131 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1132 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1133 notifyInferiorStopOk();
1134 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1135 notifyInferiorRunRequested();
1136 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1137 notifyInferiorRunOk();
1141 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1143 if (m_specialStopMode == NoSpecialStop)
1144 doInterruptInferior(CustomSpecialStop);
1145 m_customSpecialStopData.push_back(v);
1148 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1151 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1152 m_specialStopMode = sm;
1153 QString errorMessage;
1154 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1155 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1156 m_specialStopMode = oldSpecialMode;
1157 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1164 void CdbEngine::executeRunToLine(const ContextData &data)
1166 // Add one-shot breakpoint
1167 BreakpointParameters bp;
1169 bp.type =BreakpointByAddress;
1170 bp.address = data.address;
1172 bp.type =BreakpointByFileAndLine;
1173 bp.fileName = data.fileName;
1174 bp.lineNumber = data.lineNumber;
1176 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1180 void CdbEngine::executeRunToFunction(const QString &functionName)
1182 // Add one-shot breakpoint
1183 BreakpointParameters bp(BreakpointByFunction);
1184 bp.functionName = functionName;
1186 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1190 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1192 const Registers registers = registerHandler()->registers();
1193 QTC_ASSERT(regnr < registers.size(), return)
1194 // Value is decimal or 0x-hex-prefixed
1196 ByteArrayInputStream str(cmd);
1197 str << "r " << registers.at(regnr).name << '=' << value;
1198 postCommand(cmd, 0);
1202 void CdbEngine::executeJumpToLine(const ContextData &data)
1205 // Goto address directly.
1206 jumpToAddress(data.address);
1207 gotoLocation(Location(data.address));
1209 // Jump to source line: Resolve source line address and go to that location
1211 ByteArrayInputStream str(cmd);
1212 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1213 const QVariant cookie = qVariantFromValue(data);
1214 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1218 void CdbEngine::jumpToAddress(quint64 address)
1220 // Fake a jump to address by setting the PC register.
1221 QByteArray registerCmd;
1222 ByteArrayInputStream str(registerCmd);
1223 // PC-register depending on 64/32bit.
1224 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1225 str.setHexPrefix(true);
1226 str.setIntegerBase(16);
1228 postCommand(registerCmd, 0);
1231 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1233 if (cmd->reply.isEmpty())
1235 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1236 // Set register 'rip' to hex address and goto lcoation
1237 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1238 const int equalPos = answer.indexOf(" = ");
1241 answer.remove(0, equalPos + 3);
1242 answer.remove(QLatin1Char('`'));
1244 const quint64 address = answer.toLongLong(&ok, 16);
1245 if (ok && address) {
1246 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1247 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1248 jumpToAddress(address);
1249 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1253 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1256 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1258 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1259 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1264 ByteArrayInputStream str(cmd);
1265 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1266 postCommand(cmd, 0);
1267 // Update all locals in case we change a union or something pointed to
1268 // that affects other variables, too.
1272 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1274 int currentThreadId;
1275 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1276 threadsHandler()->setThreads(threads);
1277 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1278 forceCurrentThreadId : currentThreadId);
1281 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1284 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1285 if (reply->success) {
1287 data.fromString(reply->reply);
1289 // Continue sequence
1290 postCommandSequence(reply->commandSequence);
1292 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1296 void CdbEngine::executeDebuggerCommand(const QString &command)
1298 postCommand(command.toLocal8Bit(), QuietCommand);
1301 // Post command without callback
1302 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1305 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1306 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1307 if (!(flags & QuietCommand))
1308 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1309 m_process.write(cmd + '\n');
1312 // Post a built-in-command producing free-format output with a callback.
1313 // In order to catch the output, it is enclosed in 'echo' commands
1314 // printing a specially formatted token to be identifiable in the output.
1315 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1316 BuiltinCommandHandler handler,
1317 unsigned nextCommandFlag,
1318 const QVariant &cookie)
1320 if (!m_accessible) {
1321 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1322 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1323 showMessage(msg, LogError);
1326 if (!flags & QuietCommand)
1327 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1329 const int token = m_nextCommandToken++;
1330 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1332 m_builtinCommandQueue.push_back(pendingCommand);
1333 // Enclose command in echo-commands for token
1335 ByteArrayInputStream str(fullCmd);
1336 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1337 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1339 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1340 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1341 m_builtinCommandQueue.size(), nextCommandFlag);
1343 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1344 fullCmd.constData());
1345 m_process.write(fullCmd);
1348 // Post an extension command producing one-line output with a callback,
1349 // pass along token for identification in queue.
1350 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1351 const QByteArray &arguments,
1353 ExtensionCommandHandler handler,
1354 unsigned nextCommandFlag,
1355 const QVariant &cookie)
1357 if (!m_accessible) {
1358 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1359 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1360 showMessage(msg, LogError);
1364 const int token = m_nextCommandToken++;
1366 // Format full command with token to be recognizeable in the output
1368 ByteArrayInputStream str(fullCmd);
1369 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1370 if (!arguments.isEmpty())
1371 str << ' ' << arguments;
1373 if (!flags & QuietCommand)
1374 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1376 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1378 m_extensionCommandQueue.push_back(pendingCommand);
1379 // Enclose command in echo-commands for token
1381 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1382 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1383 m_extensionCommandQueue.size(), nextCommandFlag);
1384 m_process.write(fullCmd + '\n');
1387 void CdbEngine::activateFrame(int index)
1389 // TODO: assembler,etc
1392 const StackFrames &frames = stackHandler()->frames();
1393 QTC_ASSERT(index < frames.size(), return; )
1395 const StackFrame frame = frames.at(index);
1396 if (debug || debugLocals)
1397 qDebug("activateFrame idx=%d '%s' %d", index,
1398 qPrintable(frame.file), frame.line);
1399 stackHandler()->setCurrentIndex(index);
1400 const bool showAssembler = !frames.at(index).isUsable();
1401 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1402 watchHandler()->beginCycle();
1403 watchHandler()->endCycle();
1404 QAction *assemblerAction = theAssemblerAction();
1405 if (assemblerAction->isChecked()) {
1406 gotoLocation(frame);
1408 assemblerAction->trigger(); // Seems to trigger update
1411 gotoLocation(frame);
1416 void CdbEngine::updateLocals(bool forNewStackFrame)
1418 typedef QHash<QByteArray, int> WatcherHash;
1420 const int frameIndex = stackHandler()->currentIndex();
1421 if (frameIndex < 0) {
1422 watchHandler()->beginCycle();
1423 watchHandler()->endCycle();
1426 const StackFrame frame = stackHandler()->currentFrame();
1427 if (!frame.isUsable()) {
1428 watchHandler()->beginCycle();
1429 watchHandler()->endCycle();
1432 /* Watchers: Forcibly discard old symbol group as switching from
1433 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1434 * and cause errors as it seems to go 'stale' when switching threads.
1435 * Initial expand, get uninitialized and query */
1436 QByteArray arguments;
1437 ByteArrayInputStream str(arguments);
1440 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1441 if (!expanded.isEmpty()) {
1442 str << blankSeparator << "-e ";
1444 foreach(const QByteArray &e, expanded) {
1450 addLocalsOptions(str);
1451 // Uninitialized variables if desired
1452 if (debuggerCore()->boolSetting(UseCodeModel)) {
1453 QStringList uninitializedVariables;
1454 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1455 frame.function, frame.file, frame.line, &uninitializedVariables);
1456 if (!uninitializedVariables.isEmpty()) {
1457 str << blankSeparator << "-u ";
1459 foreach(const QString &u, uninitializedVariables) {
1462 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 DisassemblerLines disassemblerLines;
1511 foreach(const QByteArray &line, command->reply)
1512 disassemblerLines.appendLine(DisassemblerLine(QString::fromLatin1(line)));
1513 agent->setContents(disassemblerLines);
1516 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1518 if (!m_accessible) {
1519 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1523 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1526 ByteArrayInputStream str(args);
1527 str << addr << ' ' << length;
1528 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1529 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1532 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1534 QTC_ASSERT(!data.isEmpty(), return; )
1535 if (!m_accessible) {
1536 const MemoryChangeCookie cookie(addr, data);
1537 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1539 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1543 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1545 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1546 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1547 if (command->success) {
1548 const QByteArray data = QByteArray::fromBase64(command->reply);
1549 if (unsigned(data.size()) == memViewCookie.length)
1550 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1551 memViewCookie.address, data);
1553 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1557 void CdbEngine::reloadModules()
1559 postCommandSequence(CommandListModules);
1562 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1566 void CdbEngine::loadAllSymbols()
1570 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1572 Q_UNUSED(moduleName)
1575 void CdbEngine::reloadRegisters()
1577 postCommandSequence(CommandListRegisters);
1580 void CdbEngine::reloadSourceFiles()
1584 void CdbEngine::reloadFullStack()
1587 qDebug("%s", Q_FUNC_INFO);
1588 postCommandSequence(CommandListStack);
1591 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1593 if (reply->success) {
1594 m_inferiorPid = reply->reply.toUInt();
1595 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1596 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1597 notifyInferiorSetupOk();
1599 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1600 arg(QLatin1String(reply->errorMessage)), LogError);
1601 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1602 notifyInferiorSetupFailed();
1606 // Parse CDB gdbmi register syntax
1607 static inline Register parseRegister(const GdbMi &gdbmiReg)
1610 reg.name = gdbmiReg.findChild("name").data();
1611 const GdbMi description = gdbmiReg.findChild("description");
1612 if (description.type() != GdbMi::Invalid) {
1614 reg.name += description.data();
1617 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1621 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1623 if (reply->success) {
1625 value.fromString(reply->reply);
1626 if (value.type() == GdbMi::List) {
1628 modules.reserve(value.childCount());
1629 foreach (const GdbMi &gdbmiModule, value.children()) {
1631 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1632 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1633 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1634 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1635 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1636 module.symbolsRead = Module::ReadOk;
1637 modules.push_back(module);
1639 modulesHandler()->setModules(modules);
1641 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1642 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1645 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1646 arg(QLatin1String(reply->errorMessage)), LogError);
1648 postCommandSequence(reply->commandSequence);
1652 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1654 if (reply->success) {
1656 value.fromString(reply->reply);
1657 if (value.type() == GdbMi::List) {
1658 Registers registers;
1659 registers.reserve(value.childCount());
1660 foreach (const GdbMi &gdbmiReg, value.children())
1661 registers.push_back(parseRegister(gdbmiReg));
1662 registerHandler()->setRegisters(registers);
1664 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1665 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1668 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1669 arg(QLatin1String(reply->errorMessage)), LogError);
1671 postCommandSequence(reply->commandSequence);
1674 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1676 if (reply->success) {
1677 QList<WatchData> watchData;
1679 root.fromString(reply->reply);
1680 QTC_ASSERT(root.isList(), return ; )
1682 qDebug() << root.toString(true, 4);
1684 // Courtesy of GDB engine
1685 foreach (const GdbMi &child, root.children()) {
1687 dummy.iname = child.findChild("iname").data();
1688 dummy.name = QLatin1String(child.findChild("name").data());
1689 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1691 watchHandler()->insertBulkData(watchData);
1692 watchHandler()->endCycle();
1694 QDebug nsp = qDebug().nospace();
1695 nsp << "Obtained " << watchData.size() << " items:\n";
1696 foreach (const WatchData &wd, watchData)
1697 nsp << wd.toString() <<'\n';
1699 const bool forNewStackFrame = reply->cookie.toBool();
1700 if (forNewStackFrame)
1701 emit stackFrameCompleted();
1703 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1707 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1709 if (!reply->success)
1710 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1713 enum CdbExecutionStatus {
1714 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1715 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1716 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1717 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1718 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1719 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1720 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1722 static const char *cdbStatusName(unsigned long s)
1725 case CDB_STATUS_NO_CHANGE:
1729 case CDB_STATUS_GO_HANDLED:
1730 return "go_handled";
1731 case CDB_STATUS_GO_NOT_HANDLED:
1732 return "go_not_handled";
1733 case CDB_STATUS_STEP_OVER:
1735 case CDB_STATUS_STEP_INTO:
1737 case CDB_STATUS_BREAK:
1739 case CDB_STATUS_NO_DEBUGGEE:
1740 return "no_debuggee";
1741 case CDB_STATUS_STEP_BRANCH:
1742 return "step_branch";
1743 case CDB_STATUS_IGNORE_EVENT:
1744 return "ignore_event";
1745 case CDB_STATUS_RESTART_REQUESTED:
1746 return "restart_requested";
1747 case CDB_STATUS_REVERSE_GO:
1748 return "reverse_go";
1749 case CDB_STATUS_REVERSE_STEP_BRANCH:
1750 return "reverse_step_branch";
1751 case CDB_STATUS_REVERSE_STEP_OVER:
1752 return "reverse_step_over";
1753 case CDB_STATUS_REVERSE_STEP_INTO:
1754 return "reverse_step_into";
1759 /* Examine how to react to a stop. */
1760 enum StopActionFlags
1763 StopReportLog = 0x1,
1764 StopReportStatusMessage = 0x2,
1765 StopReportParseError = 0x4,
1766 StopShowExceptionMessageBox = 0x8,
1767 // Notify stop or just continue
1768 StopNotifyStop = 0x10,
1769 StopIgnoreContinue = 0x20,
1770 // Hit on break in artificial stop thread (created by DebugBreak()).
1771 StopInArtificialThread = 0x40,
1772 StopShutdownInProgress = 0x80 // Shutdown in progress
1775 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1776 const QString &threadId)
1778 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1779 .arg(id).arg(number).arg(threadId);
1782 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1783 const QByteArray &condition,
1784 const QString &threadId)
1786 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1787 .arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
1790 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1792 QString *exceptionBoxMessage,
1793 bool conditionalBreakPointTriggered)
1795 // Report stop reason (GDBMI)
1797 if (targetState() == DebuggerFinished)
1798 rc |= StopShutdownInProgress;
1800 qDebug("%s", stopReason.toString(true, 4).constData());
1801 const QByteArray reason = stopReason.findChild("reason").data();
1802 if (reason.isEmpty()) {
1803 *message = tr("Malformed stop response received.");
1804 rc |= StopReportParseError|StopNotifyStop;
1807 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1808 if (state() == InferiorStopOk) {
1809 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1810 arg(QString::fromAscii(reason));
1811 rc |= StopReportLog;
1814 const int threadId = stopReason.findChild("threadId").data().toInt();
1815 if (reason == "breakpoint") {
1816 // Note: Internal breakpoints (run to line) are reported with id=0.
1817 // Step out creates temporary breakpoints with id 10000.
1818 BreakpointId id = 0;
1820 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1821 if (breakpointIdG.isValid()) {
1822 id = breakpointIdG.data().toULongLong();
1823 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1824 const BreakpointResponse parameters = breakHandler()->response(id);
1825 // Trace point? Just report.
1826 number = parameters.number;
1827 if (parameters.tracepoint) {
1828 *message = msgTracePointTriggered(id, number, QString::number(threadId));
1829 return StopReportLog|StopIgnoreContinue;
1831 // Trigger evaluation of BP expression unless we are already in the response.
1832 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1833 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1834 QString::number(threadId));
1835 ConditionalBreakPointCookie cookie(id);
1836 cookie.stopReason = stopReason;
1837 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1838 return StopReportLog;
1844 if (id && breakHandler()->type(id) == Watchpoint) {
1845 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1847 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1849 rc |= StopReportStatusMessage|StopNotifyStop;
1852 if (reason == "exception") {
1853 WinException exception;
1854 exception.fromGdbMI(stopReason);
1855 QString description = exception.toString();
1857 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1858 // pulls DLLs. Avoid showing a 'stopped' Message box.
1859 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1860 || exception.exceptionCode == winExceptionWX86Breakpoint)
1861 return StopNotifyStop;
1862 if (exception.exceptionCode == winExceptionCtrlPressed) {
1863 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1864 *message = msgInterrupted();
1865 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1868 if (isDebuggerWinException(exception.exceptionCode)) {
1869 rc |= StopReportStatusMessage|StopNotifyStop;
1870 // Detect interruption by DebugBreak() and force a switch to thread 0.
1871 if (exception.function == "ntdll!DbgBreakPoint")
1872 rc |= StopInArtificialThread;
1873 *message = msgInterrupted();
1877 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1878 *message = description;
1879 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1882 *message = msgStopped(QLatin1String(reason));
1883 rc |= StopReportStatusMessage|StopNotifyStop;
1887 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1893 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1894 elapsedLogTime(), messageBA.constData(),
1895 stateName(state()), m_specialStopMode);
1897 // Switch source level debugging
1898 syncOperateByInstruction(m_operateByInstructionPending);
1900 // Engine-special stop reasons: Breakpoints and setup
1901 const SpecialStopMode specialStopMode = m_specialStopMode;
1903 m_specialStopMode = NoSpecialStop;
1905 switch(specialStopMode) {
1906 case SpecialStopSynchronizeBreakpoints:
1908 qDebug("attemptBreakpointSynchronization in special stop");
1909 attemptBreakpointSynchronization();
1910 doContinueInferior();
1912 case SpecialStopGetWidgetAt:
1913 postWidgetAtCommand();
1915 case CustomSpecialStop:
1916 foreach (const QVariant &data, m_customSpecialStopData)
1917 handleCustomSpecialStop(data);
1918 m_customSpecialStopData.clear();
1919 doContinueInferior();
1925 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1926 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1927 notifyEngineSetupOk();
1931 stopReason.fromString(messageBA);
1932 processStop(stopReason, false);
1935 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1937 // Further examine stop and report to user
1939 QString exceptionBoxMessage;
1940 int forcedThreadId = -1;
1941 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1942 conditionalBreakPointTriggered);
1943 // Do the non-blocking log reporting
1944 if (stopFlags & StopReportLog)
1945 showMessage(message, LogMisc);
1946 if (stopFlags & StopReportStatusMessage)
1947 showStatusMessage(message);
1948 if (stopFlags & StopReportParseError)
1949 showMessage(message, LogError);
1950 // Ignore things like WOW64, report tracepoints.
1951 if (stopFlags & StopIgnoreContinue) {
1952 postCommand("g", 0);
1955 // Notify about state and send off command sequence to get stack, etc.
1956 if (stopFlags & StopNotifyStop) {
1957 if (state() == InferiorStopRequested) {
1958 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1959 notifyInferiorStopOk();
1961 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1962 notifyInferiorSpontaneousStop();
1964 // Prevent further commands from being sent if shutdown is in progress
1965 if (stopFlags & StopShutdownInProgress) {
1966 showMessage(QString::fromLatin1("Shutdown request detected..."));
1969 const bool sourceStepInto = m_sourceStepInto;
1970 m_sourceStepInto = false;
1971 // Start sequence to get all relevant data.
1972 if (stopFlags & StopInArtificialThread) {
1973 showMessage(tr("Switching to main thread..."), LogMisc);
1974 postCommand("~0 s", 0);
1976 // Re-fetch stack again.
1977 postCommandSequence(CommandListStack);
1979 const GdbMi stack = stopReason.findChild("stack");
1980 if (stack.isValid()) {
1981 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1982 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1986 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1989 const GdbMi threads = stopReason.findChild("threads");
1990 if (threads.isValid()) {
1991 parseThreads(threads, forcedThreadId);
1993 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1995 // Fire off remaining commands asynchronously
1996 if (!m_pendingBreakpointMap.isEmpty())
1997 postCommandSequence(CommandListBreakPoints);
1998 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1999 postCommandSequence(CommandListRegisters);
2000 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2001 postCommandSequence(CommandListModules);
2003 // After the sequence has been sent off and CDB is pondering the commands,
2004 // pop up a message box for exceptions.
2005 if (stopFlags & StopShowExceptionMessageBox)
2006 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2009 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2011 const DebuggerState s = state();
2012 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2016 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2017 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2020 case EngineShutdownRequested:
2023 case InferiorShutdownRequested:
2031 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2033 const DebuggerState s = state();
2036 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2040 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2041 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2044 case EngineSetupRequested:
2046 case EngineRunRequested:
2047 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2048 notifyEngineRunAndInferiorRunOk();
2051 case InferiorStopOk:
2052 // Inaccessible without debuggee (exit breakpoint)
2053 // We go for spontaneous engine shutdown instead.
2054 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2056 qDebug("Lost debuggeee");
2057 m_hasDebuggee = false;
2060 case InferiorRunRequested:
2061 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2062 notifyInferiorRunOk();
2065 case EngineShutdownRequested:
2072 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2075 QDebug nospace = qDebug().nospace();
2076 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2077 << ' ' << stateName(state());
2078 if (t == 'N' || debug > 1) {
2079 nospace << ' ' << message;
2081 nospace << ' ' << message.size() << " bytes";
2085 // Is there a reply expected, some command queued?
2086 if (t == 'R' || t == 'N') {
2087 if (token == -1) { // Default token, user typed in extension command
2088 showMessage(QString::fromLatin1(message), LogMisc);
2091 const int index = indexOfCommand(m_extensionCommandQueue, token);
2093 // Did the command finish? Take off queue and complete, invoke CB
2094 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2096 command->success = true;
2097 command->reply = message;
2099 command->success = false;
2100 command->errorMessage = message;
2103 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2104 command->command.constData(), command->token, m_extensionCommandQueue.size());
2105 if (command->handler)
2106 (this->*(command->handler))(command);
2111 if (what == "debuggee_output") {
2112 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2116 if (what == "event") {
2117 showStatusMessage(QString::fromAscii(message), 5000);
2121 if (what == "session_accessible") {
2122 if (!m_accessible) {
2123 m_accessible = true;
2124 handleSessionAccessible(message.toULong());
2129 if (what == "session_inaccessible") {
2131 m_accessible = false;
2132 handleSessionInaccessible(message.toULong());
2137 if (what == "session_idle") {
2138 handleSessionIdle(message);
2142 if (what == "exception") {
2143 WinException exception;
2145 gdbmi.fromString(message);
2146 exception.fromGdbMI(gdbmi);
2147 const QString message = exception.toString(true);
2148 showStatusMessage(message);
2149 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2150 if (exception.exceptionCode == winExceptionCppException)
2151 showMessage(message + QLatin1Char('\n'), AppOutput);
2159 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2160 enum { CdbPromptLength = 7 };
2162 static inline bool isCdbPrompt(const QByteArray &c)
2164 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2165 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2166 && std::isdigit(c.at(4));
2169 // Check for '<token>32>' or '<token>32<'
2170 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2171 int *token, bool *isStart)
2175 const int tokenPrefixSize = tokenPrefix.size();
2176 const int size = c.size();
2177 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2179 switch (c.at(size - 1)) {
2189 if (!c.startsWith(tokenPrefix))
2192 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2196 void CdbEngine::parseOutputLine(QByteArray line)
2198 // The hooked output callback in the extension suppresses prompts,
2199 // it should happen only in initial and exit stages. Note however that
2200 // if the output is not hooked, sequences of prompts are possible which
2201 // can mix things up.
2202 while (isCdbPrompt(line))
2203 line.remove(0, CdbPromptLength);
2204 // An extension notification (potentially consisting of several chunks)
2205 if (line.startsWith(m_creatorExtPrefix)) {
2206 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2207 const char type = line.at(m_creatorExtPrefix.size());
2209 const int tokenPos = m_creatorExtPrefix.size() + 2;
2210 const int tokenEndPos = line.indexOf('|', tokenPos);
2211 QTC_ASSERT(tokenEndPos != -1, return)
2212 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2214 const int remainingChunksPos = tokenEndPos + 1;
2215 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2216 QTC_ASSERT(remainingChunksEndPos != -1, return)
2217 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2218 // const char 'serviceName'
2219 const int whatPos = remainingChunksEndPos + 1;
2220 const int whatEndPos = line.indexOf('|', whatPos);
2221 QTC_ASSERT(whatEndPos != -1, return)
2222 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2223 // Build up buffer, call handler once last chunk was encountered
2224 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2225 if (remainingChunks == 0) {
2226 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2227 m_extensionMessageBuffer.clear();
2231 // Check for command start/end tokens within which the builtin command
2232 // output is enclosed
2234 bool isStartToken = false;
2235 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2237 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2238 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2240 // If there is a current command, wait for end of output indicated by token,
2241 // command, trigger handler and finish, else append to its output.
2242 if (m_currentBuiltinCommandIndex != -1) {
2243 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2244 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2245 if (isCommandToken) {
2246 // Did the command finish? Invoke callback and remove from queue.
2248 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2249 currentCommand->command.constData(), currentCommand->token,
2250 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2251 QTC_ASSERT(token == currentCommand->token, return; );
2252 if (currentCommand->handler)
2253 (this->*(currentCommand->handler))(currentCommand);
2254 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2255 m_currentBuiltinCommandIndex = -1;
2257 // Record output of current command
2258 currentCommand->reply.push_back(line);
2261 } // m_currentCommandIndex
2262 if (isCommandToken) {
2263 // Beginning command token encountered, start to record output.
2264 const int index = indexOfCommand(m_builtinCommandQueue, token);
2265 QTC_ASSERT(isStartToken && index != -1, return; );
2266 m_currentBuiltinCommandIndex = index;
2267 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2269 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2273 showMessage(QString::fromLocal8Bit(line), LogMisc);
2276 void CdbEngine::readyReadStandardOut()
2278 if (m_ignoreCdbOutput)
2280 m_outputBuffer += m_process.readAllStandardOutput();
2281 // Split into lines and parse line by line.
2283 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2284 if (endOfLinePos == -1) {
2288 QByteArray line = m_outputBuffer.left(endOfLinePos);
2289 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2290 line.truncate(line.size() - 1);
2291 parseOutputLine(line);
2292 m_outputBuffer.remove(0, endOfLinePos + 1);
2297 void CdbEngine::readyReadStandardError()
2299 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2302 void CdbEngine::processError()
2304 showMessage(m_process.errorString(), LogError);
2308 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2309 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2311 QByteArray cmd(cmdC);
2312 ByteArrayInputStream str(cmd);
2313 foreach(const BreakpointData *bp, bps)
2314 str << ' ' << bp->bpNumber;
2319 bool CdbEngine::stateAcceptsBreakpointChanges() const
2323 case InferiorStopOk:
2331 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2333 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2334 if (!DebuggerEngine::isCppBreakpoint(data))
2336 switch (data.type) {
2338 case BreakpointAtFork:
2339 //case BreakpointAtVFork:
2340 case BreakpointAtSysCall:
2343 case BreakpointByFileAndLine:
2344 case BreakpointByFunction:
2345 case BreakpointByAddress:
2346 case BreakpointAtThrow:
2347 case BreakpointAtCatch:
2348 case BreakpointAtMain:
2349 case BreakpointAtExec:
2355 void CdbEngine::attemptBreakpointSynchronization()
2358 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2359 // Check if there is anything to be done at all.
2360 BreakHandler *handler = breakHandler();
2361 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2362 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2363 if (acceptsBreakpoint(id))
2364 handler->setEngine(id, this);
2366 // Quick check: is there a need to change something? - Populate module cache
2367 bool changed = false;
2368 const BreakpointIds ids = handler->engineBreakpointIds(this);
2369 foreach (BreakpointId id, ids) {
2370 switch (handler->state(id)) {
2371 case BreakpointInsertRequested:
2372 case BreakpointRemoveRequested:
2373 case BreakpointChangeRequested:
2376 case BreakpointInserted: {
2377 // Collect the new modules matching the files.
2378 // In the future, that information should be obtained from the build system.
2379 const BreakpointParameters &data = handler->breakpointData(id);
2380 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2381 m_fileNameModuleHash.insert(data.fileName, data.module);
2389 if (debugBreakpoints)
2390 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2391 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2395 if (!m_accessible) {
2397 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2398 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2401 // Add/Change breakpoints and store pending ones in map, since
2402 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2403 // handleBreakPoints will the complete that information and set it on the break handler.
2404 bool addedChanged = false;
2405 foreach (BreakpointId id, ids) {
2406 BreakpointParameters parameters = handler->breakpointData(id);
2407 BreakpointResponse response;
2408 response.fromParameters(parameters);
2409 // If we encountered that file and have a module for it: Add it.
2410 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2411 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2412 if (it != m_fileNameModuleHash.constEnd())
2413 parameters.module = it.value();
2415 switch (handler->state(id)) {
2416 case BreakpointInsertRequested:
2417 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2418 if (!parameters.enabled)
2419 postCommand("bd " + QByteArray::number(id), 0);
2420 handler->notifyBreakpointInsertProceeding(id);
2421 handler->notifyBreakpointInsertOk(id);
2422 m_pendingBreakpointMap.insert(id, response);
2423 addedChanged = true;
2424 // Ensure enabled/disabled is correct in handler and line number is there.
2425 handler->setResponse(id, response);
2426 if (debugBreakpoints)
2427 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2429 case BreakpointChangeRequested:
2430 handler->notifyBreakpointChangeProceeding(id);
2431 if (debugBreakpoints)
2432 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2433 qPrintable(parameters.toString()));
2434 if (parameters.enabled != handler->response(id).enabled) {
2435 // Change enabled/disabled breakpoints without triggering update.
2436 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2437 response.pending = false;
2438 response.enabled = parameters.enabled;
2439 handler->setResponse(id, response);
2441 // Delete and re-add, triggering update
2442 addedChanged = true;
2443 postCommand("bc " + QByteArray::number(id), 0);
2444 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2445 m_pendingBreakpointMap.insert(id, response);
2447 handler->notifyBreakpointChangeOk(id);
2449 case BreakpointRemoveRequested:
2450 postCommand("bc " + QByteArray::number(id), 0);
2451 handler->notifyBreakpointRemoveProceeding(id);
2452 handler->notifyBreakpointRemoveOk(id);
2453 m_pendingBreakpointMap.remove(id);
2459 // List breakpoints and send responses
2461 postCommandSequence(CommandListBreakPoints);
2464 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2465 // manager to correctly process it) and convert to clean path.
2466 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2469 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2470 if (it != m_normalizedFileCache.constEnd())
2472 if (debugSourceMapping)
2473 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2474 // Do we have source path mappings? ->Apply.
2475 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2477 // Up/lower case normalization according to Windows.
2479 QString normalized = winNormalizeFileName(fileName);
2481 QString normalized = fileName;
2483 if (debugSourceMapping)
2484 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2485 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2486 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2487 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2489 // At least upper case drive letter if failed.
2490 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2491 result.fileName[0] = result.fileName.at(0).toUpper();
2493 m_normalizedFileCache.insert(f, result);
2494 if (debugSourceMapping)
2495 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2499 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2500 // has more processing.
2501 static StackFrames parseFrames(const GdbMi &gdbmi)
2504 const int count = gdbmi.childCount();
2506 for (int i = 0; i < count; i++) {
2507 const GdbMi &frameMi = gdbmi.childAt(i);
2510 const GdbMi fullName = frameMi.findChild("fullname");
2511 if (fullName.isValid()) {
2512 frame.file = QFile::decodeName(fullName.data());
2513 frame.line = frameMi.findChild("line").data().toInt();
2514 frame.usable = false; // To be decided after source path mapping.
2516 frame.function = QLatin1String(frameMi.findChild("func").data());
2517 frame.from = QLatin1String(frameMi.findChild("from").data());
2518 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2519 rc.push_back(frame);
2524 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2526 // Parse frames, find current. Special handling for step into:
2527 // When stepping into on an actual function (source mode) by executing 't', an assembler
2528 // frame pointing at the jmp instruction is hit (noticeable by top function being
2529 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2530 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2532 StackFrames frames = parseFrames(data);
2533 const int count = frames.size();
2534 for (int i = 0; i < count; i++) {
2535 const bool hasFile = !frames.at(i).file.isEmpty();
2536 // jmp-frame hit by step into, do another 't' and abort sequence.
2537 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2538 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2539 return ParseStackStepInto;
2542 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2543 frames[i].file = fileName.fileName;
2544 frames[i].usable = fileName.exists;
2545 if (current == -1 && frames[i].usable)
2549 if (count && current == -1) // No usable frame, use assembly.
2552 stackHandler()->setFrames(frames);
2553 activateFrame(current);
2557 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2559 if (command->success) {
2561 data.fromString(command->reply);
2562 parseStackTrace(data, false);
2563 postCommandSequence(command->commandSequence);
2565 showMessage(command->errorMessage, LogError);
2569 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2572 if (command->success) {
2573 value = command->reply.toInt();
2575 showMessage(command->errorMessage, LogError);
2577 // Is this a conditional breakpoint?
2578 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2579 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2580 const QString message = value ?
2581 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2582 arg(value).arg(cookie.id) :
2583 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2585 showMessage(message, LogMisc);
2586 // Stop if evaluation is true, else continue
2588 processStop(cookie.stopReason, true);
2590 postCommand("g", 0);
2595 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2597 if (exp.contains(' ') && !exp.startsWith('"')) {
2601 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2604 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2606 postCommandSequence(command->commandSequence);
2609 // Post a sequence of standard commands: Trigger next once one completes successfully
2610 void CdbEngine::postCommandSequence(unsigned mask)
2613 qDebug("postCommandSequence 0x%x\n", mask);
2617 if (mask & CommandListThreads) {
2618 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2621 if (mask & CommandListStack) {
2622 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2625 if (mask & CommandListRegisters) {
2626 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2627 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2630 if (mask & CommandListModules) {
2631 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2634 if (mask & CommandListBreakPoints) {
2635 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2636 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2641 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2643 bool success = false;
2646 if (!reply->success) {
2647 message = QString::fromAscii(reply->errorMessage);
2650 // Should be "namespace::QWidget:0x555"
2651 QString watchExp = QString::fromAscii(reply->reply);
2652 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2654 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2657 // 0x000 -> nothing found
2658 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2659 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2662 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2663 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2664 watchExp.insert(0, QLatin1String("*("));
2665 watchHandler()->watchExpression(watchExp);
2669 showMessage(message, LogWarning);
2670 m_watchPointX = m_watchPointY = 0;
2673 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2676 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2680 str.setIntegerBase(16);
2681 str << ", at 0x" << r.address;
2682 str.setIntegerBase(10);
2685 str << ", disabled";
2686 if (!r.module.isEmpty())
2687 str << ", module: '" << r.module << '\'';
2691 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2693 if (debugBreakpoints)
2694 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2695 if (!reply->success) {
2696 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2700 value.fromString(reply->reply);
2701 if (value.type() != GdbMi::List) {
2702 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2705 handleBreakPoints(value);
2708 void CdbEngine::handleBreakPoints(const GdbMi &value)
2710 // Report all obtained parameters back. Note that not all parameters are reported
2711 // back, so, match by id and complete
2712 if (debugBreakpoints)
2713 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2715 QTextStream str(&message);
2716 BreakHandler *handler = breakHandler();
2717 foreach (const GdbMi &breakPointG, value.children()) {
2718 BreakpointResponse reportedResponse;
2719 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2720 if (debugBreakpoints)
2721 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2722 qPrintable(reportedResponse.toString()));
2724 if (!reportedResponse.pending) {
2725 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2726 if (it != m_pendingBreakpointMap.end()) {
2727 // Complete the response and set on handler.
2728 BreakpointResponse ¤tResponse = it.value();
2729 currentResponse.number = reportedResponse.number;
2730 currentResponse.address = reportedResponse.address;
2731 currentResponse.module = reportedResponse.module;
2732 currentResponse.pending = reportedResponse.pending;
2733 currentResponse.enabled = reportedResponse.enabled;
2734 formatCdbBreakPointResponse(id, currentResponse, str);
2735 if (debugBreakpoints)
2736 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2737 handler->setResponse(id, currentResponse);
2738 m_pendingBreakpointMap.erase(it);
2740 } // not pending reported
2742 if (m_pendingBreakpointMap.empty()) {
2743 str << QLatin1String("All breakpoints have been resolved.\n");
2745 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2747 showMessage(message, LogMisc);
2750 void CdbEngine::watchPoint(const QPoint &p)
2752 m_watchPointX = p.x();
2753 m_watchPointY = p.y();
2755 case InferiorStopOk:
2756 postWidgetAtCommand();
2759 // "Select Widget to Watch" from a running application is currently not
2760 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2761 // but requires some work as not to confuse the engine by state-change notifications
2762 // emitted by the debuggee function call.
2763 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2766 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2767 arg(QString::fromAscii(stateName(state()))), LogWarning);
2772 void CdbEngine::postWidgetAtCommand()
2774 QByteArray arguments = QByteArray::number(m_watchPointX);
2775 arguments.append(' ');
2776 arguments.append(QByteArray::number(m_watchPointY));
2777 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2780 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2782 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2783 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2784 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2789 } // namespace Internal
2790 } // namespace Debugger