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 "debuggerinternalconstants.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)
321 // Accessed by RunControlFactory
322 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
323 DebuggerEngine *masterEngine, QString *errorMessage)
326 CdbOptionsPage *op = CdbOptionsPage::instance();
327 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
328 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
331 return new CdbEngine(sp, masterEngine, op->options());
333 Q_UNUSED(masterEngine)
336 *errorMessage = QString::fromLatin1("Unsupported debug mode");
340 bool isCdbEngineEnabled()
343 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
349 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
351 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
354 static QString cdbBinary(const DebuggerStartParameters &sp)
356 if (!sp.debuggerCommand.isEmpty()) {
357 // Do not use a GDB binary if we got started for a project with MinGW runtime.
358 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
359 && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
360 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
361 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
363 return sp.debuggerCommand;
365 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
368 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
371 if (!isCdbEngineEnabled()) {
372 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
373 arg(sp.toolChainAbi.toString()));
374 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
375 check->settingsPage = CdbOptionsPage::settingsId();
379 if (!validMode(sp.startMode)) {
380 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
384 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
385 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
386 arg(sp.toolChainAbi.toString()));
390 if (cdbBinary(sp).isEmpty()) {
391 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
392 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
393 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
400 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
405 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
408 opts->push_back(new CdbOptionsPage);
414 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
416 static inline Utils::SavedAction *theAssemblerAction()
418 return debuggerCore()->action(OperateByInstruction);
421 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
422 DebuggerEngine *masterEngine, const OptionsPtr &options) :
423 DebuggerEngine(sp, masterEngine),
424 m_creatorExtPrefix("<qtcreatorcdbext>|"),
425 m_tokenPrefix("<token>"),
427 m_effectiveStartMode(NoStartMode),
430 m_specialStopMode(NoSpecialStop),
431 m_nextCommandToken(0),
432 m_currentBuiltinCommandIndex(-1),
433 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
434 m_operateByInstructionPending(true),
435 m_operateByInstruction(true), // Default CDB setting
436 m_notifyEngineShutdownOnTermination(false),
437 m_hasDebuggee(false),
439 m_sourceStepInto(false),
442 m_ignoreCdbOutput(false)
444 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
446 setObjectName(QLatin1String("CdbEngine"));
447 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
448 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
449 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
450 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
453 void CdbEngine::init()
455 m_effectiveStartMode = NoStartMode;
457 m_accessible = false;
458 m_specialStopMode = NoSpecialStop;
459 m_nextCommandToken = 0;
460 m_currentBuiltinCommandIndex = -1;
461 m_operateByInstructionPending = theAssemblerAction()->isChecked();
462 m_operateByInstruction = true; // Default CDB setting
463 m_notifyEngineShutdownOnTermination = false;
464 m_hasDebuggee = false;
465 m_sourceStepInto = false;
466 m_watchPointX = m_watchPointY = 0;
467 m_ignoreCdbOutput = false;
469 m_outputBuffer.clear();
470 m_builtinCommandQueue.clear();
471 m_extensionCommandQueue.clear();
472 m_extensionMessageBuffer.clear();
473 m_pendingBreakpointMap.clear();
474 m_customSpecialStopData.clear();
476 // Create local list of mappings in native separators
477 m_sourcePathMappings.clear();
478 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
479 if (!globalOptions->sourcePathMap.isEmpty()) {
480 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
481 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
482 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
483 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
484 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
485 QDir::toNativeSeparators(it.value())));
488 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
491 CdbEngine::~CdbEngine()
495 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
497 // To be set next time session becomes accessible
498 m_operateByInstructionPending = operateByInstruction;
499 if (state() == InferiorStopOk)
500 syncOperateByInstruction(operateByInstruction);
503 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
506 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
507 if (m_operateByInstruction == operateByInstruction)
509 QTC_ASSERT(m_accessible, return; )
510 m_operateByInstruction = operateByInstruction;
511 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
512 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
515 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
516 TextEditor::ITextEditor *editor,
517 const DebuggerToolTipContext &contextIn)
520 qDebug() << Q_FUNC_INFO;
521 // Need a stopped debuggee and a cpp file in a valid frame
522 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
524 // Determine expression and function
527 DebuggerToolTipContext context = contextIn;
528 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
529 // Are we in the current stack frame
530 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
532 // No numerical or any other expressions [yet]
533 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
535 // Can this be found as a local variable?
536 const QByteArray localsPrefix(localsPrefixC);
537 QByteArray iname = localsPrefix + exp.toAscii();
538 QModelIndex index = watchHandler()->itemIndex(iname);
539 if (!index.isValid()) {
540 // Nope, try a 'local.this.m_foo'.
541 exp.prepend(QLatin1String("this."));
542 iname.insert(localsPrefix.size(), "this.");
543 index = watchHandler()->itemIndex(iname);
544 if (!index.isValid())
547 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
548 tw->setContext(context);
549 tw->setDebuggerModel(LocalsWatch);
550 tw->setExpression(exp);
551 tw->acquireEngine(this);
552 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
556 // Determine full path to the CDB extension library.
557 QString CdbEngine::extensionLibraryName(bool is64Bit)
559 // Determine extension lib name and path to use
561 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
562 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
563 << '/' << QT_CREATOR_CDB_EXT << ".dll";
567 // Determine environment for CDB.exe, start out with run config and
568 // add CDB extension path merged with system value should there be one.
569 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
570 QString cdbExtensionPath)
572 // Determine CDB extension path from Qt Creator
573 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
574 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
575 if (!oldCdbExtensionPath.isEmpty()) {
576 cdbExtensionPath.append(QLatin1Char(';'));
577 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
579 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
580 // config, just to make sure, delete any existing entries
581 const QString cdbExtensionPathVariableAssign =
582 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
583 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
584 if (it->startsWith(cdbExtensionPathVariableAssign)) {
585 it = runConfigEnvironment.erase(it);
591 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
592 QDir::toNativeSeparators(cdbExtensionPath));
593 return runConfigEnvironment;
596 int CdbEngine::elapsedLogTime() const
598 const int elapsed = m_logTime.elapsed();
599 const int delta = elapsed - m_elapsedLogTime;
600 m_elapsedLogTime = elapsed;
604 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
605 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
608 qDebug("startConsole %s", qPrintable(sp.executable));
609 m_consoleStub.reset(new Utils::ConsoleProcess);
610 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
611 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
612 SLOT(consoleStubMessage(QString, bool)));
613 connect(m_consoleStub.data(), SIGNAL(processStarted()),
614 SLOT(consoleStubProcessStarted()));
615 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
616 SLOT(consoleStubExited()));
617 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
618 if (sp.environment.size())
619 m_consoleStub->setEnvironment(sp.environment);
620 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
621 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
627 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
630 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
632 if (state() == EngineSetupRequested) {
633 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
634 notifyEngineSetupFailed();
636 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
639 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
641 showMessage(msg, AppOutput);
645 void CdbEngine::consoleStubProcessStarted()
648 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
649 // Attach to console process.
650 DebuggerStartParameters attachParameters = startParameters();
651 attachParameters.executable.clear();
652 attachParameters.processArgs.clear();
653 attachParameters.attachPID = m_consoleStub->applicationPID();
654 attachParameters.startMode = AttachExternal;
655 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
656 QString errorMessage;
657 if (!launchCDB(attachParameters, &errorMessage)) {
658 showMessage(errorMessage, LogError);
659 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
660 notifyEngineSetupFailed();
664 void CdbEngine::consoleStubExited()
668 void CdbEngine::setupEngine()
671 qDebug(">setupEngine");
672 // Nag to add symbol server
673 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
674 &(m_options->symbolPaths)))
675 m_options->toSettings(Core::ICore::instance()->settings());
678 if (!m_logTime.elapsed())
680 QString errorMessage;
681 // Console: Launch the stub with the suspended application and attach to it
682 // CDB in theory has a command line option '-2' that launches a
683 // console, too, but that immediately closes when the debuggee quits.
684 // Use the Creator stub instead.
685 const DebuggerStartParameters &sp = startParameters();
686 const bool launchConsole = isConsole(sp);
687 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
688 const bool ok = launchConsole ?
689 startConsole(startParameters(), &errorMessage) :
690 launchCDB(startParameters(), &errorMessage);
692 qDebug("<setupEngine ok=%d", ok);
694 showMessage(errorMessage, LogError);
695 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
696 notifyEngineSetupFailed();
700 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
703 qDebug("launchCDB startMode=%d", sp.startMode);
704 const QChar blank(QLatin1Char(' '));
705 // Start engine which will run until initial breakpoint:
706 // Determine binary (force MSVC), extension lib name and path to use
707 // The extension is passed as relative name with the path variable set
708 //(does not work with absolute path names)
709 const QString executable = cdbBinary(sp);
710 if (executable.isEmpty()) {
711 *errorMessage = tr("There is no CDB executable specified.");
717 Utils::winIs64BitBinary(executable);
721 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
722 if (!extensionFi.isFile()) {
723 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
724 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
727 const QString extensionFileName = extensionFi.fileName();
729 QStringList arguments;
730 const bool isRemote = sp.startMode == AttachToRemote;
731 if (isRemote) { // Must be first
732 arguments << QLatin1String("-remote") << sp.remoteChannel;
734 arguments << (QLatin1String("-a") + extensionFileName);
736 // Source line info/No terminal breakpoint / Pull extension
737 arguments << QLatin1String("-lines") << QLatin1String("-G")
738 // register idle (debuggee stop) notification
739 << QLatin1String("-c")
740 << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
741 if (sp.useTerminal) // Separate console
742 arguments << QLatin1String("-2");
743 if (!m_options->symbolPaths.isEmpty())
744 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
745 if (!m_options->sourcePaths.isEmpty())
746 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
747 // Compile argument string preserving quotes
748 QString nativeArguments = m_options->additionalArguments;
749 switch (sp.startMode) {
752 if (!nativeArguments.isEmpty())
753 nativeArguments.push_back(blank);
754 nativeArguments += QDir::toNativeSeparators(sp.executable);
759 case AttachCrashedExternal:
760 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
761 if (sp.startMode == AttachCrashedExternal)
762 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
765 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
768 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
769 if (!nativeArguments.isEmpty())
770 nativeArguments.push_back(blank);
771 nativeArguments += sp.processArgs;
774 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
775 arg(QDir::toNativeSeparators(executable),
776 arguments.join(QString(blank)) + blank + nativeArguments,
777 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
778 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
779 showMessage(msg, LogMisc);
781 m_outputBuffer.clear();
782 const QStringList environment = sp.environment.size() == 0 ?
783 QProcessEnvironment::systemEnvironment().toStringList() :
784 sp.environment.toStringList();
785 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
786 if (!sp.workingDirectory.isEmpty())
787 m_process.setWorkingDirectory(sp.workingDirectory);
790 if (!nativeArguments.isEmpty()) // Appends
791 m_process.setNativeArguments(nativeArguments);
793 m_process.start(executable, arguments);
794 if (!m_process.waitForStarted()) {
795 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
796 arg(QDir::toNativeSeparators(executable), m_process.errorString());
800 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
802 const unsigned long pid = 0;
804 showMessage(QString::fromLatin1("%1 running as %2").
805 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
806 m_hasDebuggee = true;
807 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
809 const QByteArray loadCommand = QByteArray(".load ")
810 + extensionFileName.toLocal8Bit();
811 postCommand(loadCommand, 0);
812 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
813 notifyEngineSetupOk();
818 void CdbEngine::setupInferior()
821 qDebug("setupInferior");
822 attemptBreakpointSynchronization();
823 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
824 postCommand(".asm source_line", 0); // Source line in assembly
825 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
828 void CdbEngine::runEngine()
832 // Resume the threads frozen by the console stub.
833 if (isConsole(startParameters()))
834 postCommand("~* m", 0);
835 foreach (const QString &breakEvent, m_options->breakEvents)
836 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
840 bool CdbEngine::commandsPending() const
842 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
845 void CdbEngine::shutdownInferior()
848 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
849 isCdbProcessRunning());
851 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
853 qDebug("notifyInferiorShutdownOk");
854 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
855 notifyInferiorShutdownOk();
860 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
862 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
863 notifyInferiorShutdownOk();
865 // A command got stuck.
866 if (commandsPending()) {
867 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
868 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
869 notifyInferiorShutdownFailed();
872 if (!canInterruptInferior()) {
873 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
874 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
875 notifyInferiorShutdownFailed();
878 interruptInferior(); // Calls us again
882 /* shutdownEngine/processFinished:
883 * Note that in the case of launching a process by the debugger, the debugger
884 * automatically quits a short time after reporting the session becoming
885 * inaccessible without debuggee (notifyInferiorExited). In that case,
886 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
887 * as not to confuse the state engine.
890 void CdbEngine::shutdownEngine()
893 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
894 "accessible=%d,commands pending=%d",
895 stateName(state()), isCdbProcessRunning(), m_accessible,
898 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
899 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
900 notifyEngineShutdownOk();
904 // No longer trigger anything from messages
905 m_ignoreCdbOutput = true;
906 // Go for kill if there are commands pending.
907 if (m_accessible && !commandsPending()) {
908 // detach: Wait for debugger to finish.
909 if (m_effectiveStartMode == AttachExternal)
911 // Remote requires a bit more force to quit.
912 if (m_effectiveStartMode == AttachToRemote) {
913 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
914 postCommand("qq", 0);
918 m_notifyEngineShutdownOnTermination = true;
921 // Remote process. No can do, currently
922 m_notifyEngineShutdownOnTermination = true;
923 Utils::SynchronousProcess::stopProcess(m_process);
926 // Lost debuggee, debugger should quit anytime now
927 if (!m_hasDebuggee) {
928 m_notifyEngineShutdownOnTermination = true;
934 void CdbEngine::processFinished()
937 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
938 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
939 m_process.exitStatus(), m_process.exitCode());
941 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
943 showMessage(tr("CDB crashed"), LogError); // not in your life.
945 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
948 if (m_notifyEngineShutdownOnTermination) {
951 qDebug("notifyEngineIll");
952 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
955 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
956 notifyEngineShutdownOk();
959 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
960 // Otherwise, we take a shortcut.
961 if (isSlaveEngine()) {
962 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
963 notifyInferiorExited();
965 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
966 notifyEngineSpontaneousShutdown();
971 void CdbEngine::detachDebugger()
973 postCommand(".detach", 0);
976 static inline bool isWatchIName(const QByteArray &iname)
978 return iname.startsWith("watch");
981 void CdbEngine::updateWatchData(const WatchData &dataIn,
982 const WatchUpdateFlags & flags)
984 if (debug || debugLocals || debugWatches)
985 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
986 elapsedLogTime(), m_accessible, stateName(state()),
987 flags.tryIncremental,
988 qPrintable(dataIn.toString()));
990 if (!m_accessible) // Add watch data while running?
994 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
996 ByteArrayInputStream str(args);
997 str << dataIn.iname << " \"" << dataIn.exp << '"';
998 postExtensionCommand("addwatch", args, 0,
999 &CdbEngine::handleAddWatch, 0,
1000 qVariantFromValue(dataIn));
1004 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
1005 WatchData data = dataIn;
1006 data.setAllUnneeded();
1007 watchHandler()->insertData(data);
1010 updateLocalVariable(dataIn.iname);
1013 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1015 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1017 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1018 if (reply->success) {
1019 updateLocalVariable(item.iname);
1021 item.setError(tr("Unable to add expression"));
1022 watchHandler()->insertData(item);
1023 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1024 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1025 reply->errorMessage), LogError);
1029 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1031 if (debuggerCore()->boolSetting(VerboseLog))
1032 str << blankSeparator << "-v";
1033 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1034 str << blankSeparator << "-c";
1035 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1036 if (!typeFormats.isEmpty())
1037 str << blankSeparator << "-T " << typeFormats;
1038 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1039 if (!individualFormats.isEmpty())
1040 str << blankSeparator << "-I " << individualFormats;
1043 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1045 const bool isWatch = isWatchIName(iname);
1047 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1048 QByteArray localsArguments;
1049 ByteArrayInputStream str(localsArguments);
1050 addLocalsOptions(str);
1052 const int stackFrame = stackHandler()->currentIndex();
1053 if (stackFrame < 0) {
1054 qWarning("Internal error; no stack frame in updateLocalVariable");
1057 str << blankSeparator << stackFrame;
1059 str << blankSeparator << iname;
1060 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1063 unsigned CdbEngine::debuggerCapabilities() const
1065 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1066 |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
1067 |ReloadModuleCapability
1068 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1069 |BreakConditionCapability|TracePointCapability
1070 |BreakModuleCapability;
1073 void CdbEngine::executeStep()
1075 if (!m_operateByInstruction)
1076 m_sourceStepInto = true; // See explanation at handleStackTrace().
1077 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1078 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1079 notifyInferiorRunRequested();
1082 void CdbEngine::executeStepOut()
1084 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1085 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1086 notifyInferiorRunRequested();
1089 void CdbEngine::executeNext()
1091 postCommand(QByteArray("p"), 0); // Step over -> p
1092 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1093 notifyInferiorRunRequested();
1096 void CdbEngine::executeStepI()
1101 void CdbEngine::executeNextI()
1106 void CdbEngine::continueInferior()
1108 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1109 notifyInferiorRunRequested();
1110 doContinueInferior();
1113 void CdbEngine::doContinueInferior()
1115 postCommand(QByteArray("g"), 0);
1118 bool CdbEngine::canInterruptInferior() const
1120 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1123 void CdbEngine::interruptInferior()
1126 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1127 if (canInterruptInferior()) {
1128 doInterruptInferior(NoSpecialStop);
1130 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1131 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1132 notifyInferiorStopOk();
1133 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1134 notifyInferiorRunRequested();
1135 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1136 notifyInferiorRunOk();
1140 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1142 if (m_specialStopMode == NoSpecialStop)
1143 doInterruptInferior(CustomSpecialStop);
1144 m_customSpecialStopData.push_back(v);
1147 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1150 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1151 m_specialStopMode = sm;
1152 QString errorMessage;
1153 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1154 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1155 m_specialStopMode = oldSpecialMode;
1156 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1163 void CdbEngine::executeRunToLine(const ContextData &data)
1165 // Add one-shot breakpoint
1166 BreakpointParameters bp;
1168 bp.type =BreakpointByAddress;
1169 bp.address = data.address;
1171 bp.type =BreakpointByFileAndLine;
1172 bp.fileName = data.fileName;
1173 bp.lineNumber = data.lineNumber;
1175 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1179 void CdbEngine::executeRunToFunction(const QString &functionName)
1181 // Add one-shot breakpoint
1182 BreakpointParameters bp(BreakpointByFunction);
1183 bp.functionName = functionName;
1185 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1189 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1191 const Registers registers = registerHandler()->registers();
1192 QTC_ASSERT(regnr < registers.size(), return)
1193 // Value is decimal or 0x-hex-prefixed
1195 ByteArrayInputStream str(cmd);
1196 str << "r " << registers.at(regnr).name << '=' << value;
1197 postCommand(cmd, 0);
1201 void CdbEngine::executeJumpToLine(const ContextData &data)
1204 // Goto address directly.
1205 jumpToAddress(data.address);
1206 gotoLocation(Location(data.address));
1208 // Jump to source line: Resolve source line address and go to that location
1210 ByteArrayInputStream str(cmd);
1211 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1212 const QVariant cookie = qVariantFromValue(data);
1213 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1217 void CdbEngine::jumpToAddress(quint64 address)
1219 // Fake a jump to address by setting the PC register.
1220 QByteArray registerCmd;
1221 ByteArrayInputStream str(registerCmd);
1222 // PC-register depending on 64/32bit.
1223 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1224 str.setHexPrefix(true);
1225 str.setIntegerBase(16);
1227 postCommand(registerCmd, 0);
1230 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1232 if (cmd->reply.isEmpty())
1234 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1235 // Set register 'rip' to hex address and goto lcoation
1236 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1237 const int equalPos = answer.indexOf(" = ");
1240 answer.remove(0, equalPos + 3);
1241 answer.remove(QLatin1Char('`'));
1243 const quint64 address = answer.toLongLong(&ok, 16);
1244 if (ok && address) {
1245 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1246 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1247 jumpToAddress(address);
1248 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1252 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1255 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1257 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1258 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1263 ByteArrayInputStream str(cmd);
1264 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1265 postCommand(cmd, 0);
1266 // Update all locals in case we change a union or something pointed to
1267 // that affects other variables, too.
1271 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1273 int currentThreadId;
1274 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1275 threadsHandler()->setThreads(threads);
1276 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1277 forceCurrentThreadId : currentThreadId);
1280 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1283 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1284 if (reply->success) {
1286 data.fromString(reply->reply);
1288 // Continue sequence
1289 postCommandSequence(reply->commandSequence);
1291 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1295 void CdbEngine::executeDebuggerCommand(const QString &command)
1297 postCommand(command.toLocal8Bit(), QuietCommand);
1300 // Post command without callback
1301 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1304 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1305 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1306 if (!(flags & QuietCommand))
1307 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1308 m_process.write(cmd + '\n');
1311 // Post a built-in-command producing free-format output with a callback.
1312 // In order to catch the output, it is enclosed in 'echo' commands
1313 // printing a specially formatted token to be identifiable in the output.
1314 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1315 BuiltinCommandHandler handler,
1316 unsigned nextCommandFlag,
1317 const QVariant &cookie)
1319 if (!m_accessible) {
1320 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1321 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1322 showMessage(msg, LogError);
1325 if (!flags & QuietCommand)
1326 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1328 const int token = m_nextCommandToken++;
1329 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1331 m_builtinCommandQueue.push_back(pendingCommand);
1332 // Enclose command in echo-commands for token
1334 ByteArrayInputStream str(fullCmd);
1335 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1336 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1338 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1339 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1340 m_builtinCommandQueue.size(), nextCommandFlag);
1342 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1343 fullCmd.constData());
1344 m_process.write(fullCmd);
1347 // Post an extension command producing one-line output with a callback,
1348 // pass along token for identification in queue.
1349 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1350 const QByteArray &arguments,
1352 ExtensionCommandHandler handler,
1353 unsigned nextCommandFlag,
1354 const QVariant &cookie)
1356 if (!m_accessible) {
1357 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1358 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1359 showMessage(msg, LogError);
1363 const int token = m_nextCommandToken++;
1365 // Format full command with token to be recognizeable in the output
1367 ByteArrayInputStream str(fullCmd);
1368 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1369 if (!arguments.isEmpty())
1370 str << ' ' << arguments;
1372 if (!flags & QuietCommand)
1373 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1375 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1377 m_extensionCommandQueue.push_back(pendingCommand);
1378 // Enclose command in echo-commands for token
1380 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1381 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1382 m_extensionCommandQueue.size(), nextCommandFlag);
1383 m_process.write(fullCmd + '\n');
1386 void CdbEngine::activateFrame(int index)
1388 // TODO: assembler,etc
1391 const StackFrames &frames = stackHandler()->frames();
1392 QTC_ASSERT(index < frames.size(), return; )
1394 const StackFrame frame = frames.at(index);
1395 if (debug || debugLocals)
1396 qDebug("activateFrame idx=%d '%s' %d", index,
1397 qPrintable(frame.file), frame.line);
1398 stackHandler()->setCurrentIndex(index);
1399 const bool showAssembler = !frames.at(index).isUsable();
1400 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1401 watchHandler()->beginCycle();
1402 watchHandler()->endCycle();
1403 QAction *assemblerAction = theAssemblerAction();
1404 if (assemblerAction->isChecked()) {
1405 gotoLocation(frame);
1407 assemblerAction->trigger(); // Seems to trigger update
1410 gotoLocation(frame);
1415 void CdbEngine::updateLocals(bool forNewStackFrame)
1417 typedef QHash<QByteArray, int> WatcherHash;
1419 const int frameIndex = stackHandler()->currentIndex();
1420 if (frameIndex < 0) {
1421 watchHandler()->beginCycle();
1422 watchHandler()->endCycle();
1425 const StackFrame frame = stackHandler()->currentFrame();
1426 if (!frame.isUsable()) {
1427 watchHandler()->beginCycle();
1428 watchHandler()->endCycle();
1431 /* Watchers: Forcibly discard old symbol group as switching from
1432 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1433 * and cause errors as it seems to go 'stale' when switching threads.
1434 * Initial expand, get uninitialized and query */
1435 QByteArray arguments;
1436 ByteArrayInputStream str(arguments);
1439 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1440 if (!expanded.isEmpty()) {
1441 str << blankSeparator << "-e ";
1443 foreach(const QByteArray &e, expanded) {
1449 addLocalsOptions(str);
1450 // Uninitialized variables if desired. Quote as safeguard against shadowed
1451 // variables in case of errors in uninitializedVariables().
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;
1467 // Perform watches synchronization
1468 str << blankSeparator << "-W";
1469 const WatcherHash watcherHash = WatchHandler::watcherNames();
1470 if (!watcherHash.isEmpty()) {
1471 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1472 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1473 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1477 // Required arguments: frame
1478 str << blankSeparator << frameIndex;
1479 watchHandler()->beginCycle();
1480 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1483 void CdbEngine::selectThread(int index)
1485 if (index < 0 || index == threadsHandler()->currentThread())
1489 const int newThreadId = threadsHandler()->threads().at(index).id;
1490 threadsHandler()->setCurrentThread(index);
1492 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1493 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1496 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1498 QTC_ASSERT(m_accessible, return;)
1500 ByteArrayInputStream str(cmd);
1501 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1502 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1503 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1506 // Parse: "00000000`77606060 cc int 3"
1507 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1509 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1510 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1511 agent->setContents(parseCdbDisassembler(command->reply));
1514 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1517 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1518 const MemoryViewCookie cookie(agent, editor, addr, length);
1520 postFetchMemory(cookie);
1522 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1526 void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
1529 ByteArrayInputStream str(args);
1530 str << cookie.address << ' ' << cookie.length;
1531 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0,
1532 qVariantFromValue(cookie));
1535 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1537 QTC_ASSERT(!data.isEmpty(), return; )
1538 if (!m_accessible) {
1539 const MemoryChangeCookie cookie(addr, data);
1540 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1542 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1546 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1548 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1549 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1550 if (command->success) {
1551 const QByteArray data = QByteArray::fromBase64(command->reply);
1552 if (unsigned(data.size()) == memViewCookie.length)
1553 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1554 memViewCookie.address, data);
1556 showMessage(QString::fromLocal8Bit(command->errorMessage), LogWarning);
1560 void CdbEngine::reloadModules()
1562 postCommandSequence(CommandListModules);
1565 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1569 void CdbEngine::loadAllSymbols()
1573 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1575 Q_UNUSED(moduleName)
1578 void CdbEngine::reloadRegisters()
1580 postCommandSequence(CommandListRegisters);
1583 void CdbEngine::reloadSourceFiles()
1587 void CdbEngine::reloadFullStack()
1590 qDebug("%s", Q_FUNC_INFO);
1591 postCommandSequence(CommandListStack);
1594 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1596 if (reply->success) {
1597 m_inferiorPid = reply->reply.toUInt();
1598 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1599 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1600 notifyInferiorSetupOk();
1602 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1603 arg(QLatin1String(reply->errorMessage)), LogError);
1604 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1605 notifyInferiorSetupFailed();
1609 // Parse CDB gdbmi register syntax
1610 static inline Register parseRegister(const GdbMi &gdbmiReg)
1613 reg.name = gdbmiReg.findChild("name").data();
1614 const GdbMi description = gdbmiReg.findChild("description");
1615 if (description.type() != GdbMi::Invalid) {
1617 reg.name += description.data();
1620 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1624 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1626 if (reply->success) {
1628 value.fromString(reply->reply);
1629 if (value.type() == GdbMi::List) {
1631 modules.reserve(value.childCount());
1632 foreach (const GdbMi &gdbmiModule, value.children()) {
1634 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1635 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1636 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1637 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1638 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1639 module.symbolsRead = Module::ReadOk;
1640 modules.push_back(module);
1642 modulesHandler()->setModules(modules);
1644 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1645 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1648 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1649 arg(QLatin1String(reply->errorMessage)), LogError);
1651 postCommandSequence(reply->commandSequence);
1655 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1657 if (reply->success) {
1659 value.fromString(reply->reply);
1660 if (value.type() == GdbMi::List) {
1661 Registers registers;
1662 registers.reserve(value.childCount());
1663 foreach (const GdbMi &gdbmiReg, value.children())
1664 registers.push_back(parseRegister(gdbmiReg));
1665 registerHandler()->setAndMarkRegisters(registers);
1667 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1668 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1671 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1672 arg(QLatin1String(reply->errorMessage)), LogError);
1674 postCommandSequence(reply->commandSequence);
1677 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1679 if (reply->success) {
1680 QList<WatchData> watchData;
1682 root.fromString(reply->reply);
1683 QTC_ASSERT(root.isList(), return ; )
1685 qDebug() << root.toString(true, 4);
1687 // Courtesy of GDB engine
1688 foreach (const GdbMi &child, root.children()) {
1690 dummy.iname = child.findChild("iname").data();
1691 dummy.name = QLatin1String(child.findChild("name").data());
1692 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1694 watchHandler()->insertBulkData(watchData);
1695 watchHandler()->endCycle();
1697 QDebug nsp = qDebug().nospace();
1698 nsp << "Obtained " << watchData.size() << " items:\n";
1699 foreach (const WatchData &wd, watchData)
1700 nsp << wd.toString() <<'\n';
1702 const bool forNewStackFrame = reply->cookie.toBool();
1703 if (forNewStackFrame)
1704 emit stackFrameCompleted();
1706 showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1710 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1712 if (!reply->success)
1713 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1716 enum CdbExecutionStatus {
1717 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1718 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1719 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1720 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1721 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1722 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1723 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1725 static const char *cdbStatusName(unsigned long s)
1728 case CDB_STATUS_NO_CHANGE:
1732 case CDB_STATUS_GO_HANDLED:
1733 return "go_handled";
1734 case CDB_STATUS_GO_NOT_HANDLED:
1735 return "go_not_handled";
1736 case CDB_STATUS_STEP_OVER:
1738 case CDB_STATUS_STEP_INTO:
1740 case CDB_STATUS_BREAK:
1742 case CDB_STATUS_NO_DEBUGGEE:
1743 return "no_debuggee";
1744 case CDB_STATUS_STEP_BRANCH:
1745 return "step_branch";
1746 case CDB_STATUS_IGNORE_EVENT:
1747 return "ignore_event";
1748 case CDB_STATUS_RESTART_REQUESTED:
1749 return "restart_requested";
1750 case CDB_STATUS_REVERSE_GO:
1751 return "reverse_go";
1752 case CDB_STATUS_REVERSE_STEP_BRANCH:
1753 return "reverse_step_branch";
1754 case CDB_STATUS_REVERSE_STEP_OVER:
1755 return "reverse_step_over";
1756 case CDB_STATUS_REVERSE_STEP_INTO:
1757 return "reverse_step_into";
1762 /* Examine how to react to a stop. */
1763 enum StopActionFlags
1766 StopReportLog = 0x1,
1767 StopReportStatusMessage = 0x2,
1768 StopReportParseError = 0x4,
1769 StopShowExceptionMessageBox = 0x8,
1770 // Notify stop or just continue
1771 StopNotifyStop = 0x10,
1772 StopIgnoreContinue = 0x20,
1773 // Hit on break in artificial stop thread (created by DebugBreak()).
1774 StopInArtificialThread = 0x40,
1775 StopShutdownInProgress = 0x80 // Shutdown in progress
1778 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1779 const QString &threadId)
1781 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1782 .arg(id).arg(number).arg(threadId);
1785 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1786 const QByteArray &condition,
1787 const QString &threadId)
1789 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1790 .arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
1793 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1795 QString *exceptionBoxMessage,
1796 bool conditionalBreakPointTriggered)
1798 // Report stop reason (GDBMI)
1800 if (targetState() == DebuggerFinished)
1801 rc |= StopShutdownInProgress;
1803 qDebug("%s", stopReason.toString(true, 4).constData());
1804 const QByteArray reason = stopReason.findChild("reason").data();
1805 if (reason.isEmpty()) {
1806 *message = tr("Malformed stop response received.");
1807 rc |= StopReportParseError|StopNotifyStop;
1810 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1811 if (state() == InferiorStopOk) {
1812 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1813 arg(QString::fromAscii(reason));
1814 rc |= StopReportLog;
1817 const int threadId = stopReason.findChild("threadId").data().toInt();
1818 if (reason == "breakpoint") {
1819 // Note: Internal breakpoints (run to line) are reported with id=0.
1820 // Step out creates temporary breakpoints with id 10000.
1821 BreakpointId id = 0;
1823 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1824 if (breakpointIdG.isValid()) {
1825 id = breakpointIdG.data().toULongLong();
1826 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1827 const BreakpointResponse parameters = breakHandler()->response(id);
1828 // Trace point? Just report.
1829 number = parameters.number;
1830 if (parameters.tracepoint) {
1831 *message = msgTracePointTriggered(id, number, QString::number(threadId));
1832 return StopReportLog|StopIgnoreContinue;
1834 // Trigger evaluation of BP expression unless we are already in the response.
1835 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1836 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1837 QString::number(threadId));
1838 ConditionalBreakPointCookie cookie(id);
1839 cookie.stopReason = stopReason;
1840 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1841 return StopReportLog;
1847 if (id && breakHandler()->type(id) == Watchpoint) {
1848 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1850 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1852 rc |= StopReportStatusMessage|StopNotifyStop;
1855 if (reason == "exception") {
1856 WinException exception;
1857 exception.fromGdbMI(stopReason);
1858 QString description = exception.toString();
1860 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1861 // pulls DLLs. Avoid showing a 'stopped' Message box.
1862 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1863 || exception.exceptionCode == winExceptionWX86Breakpoint)
1864 return StopNotifyStop;
1865 if (exception.exceptionCode == winExceptionCtrlPressed) {
1866 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1867 *message = msgInterrupted();
1868 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1871 if (isDebuggerWinException(exception.exceptionCode)) {
1872 rc |= StopReportStatusMessage|StopNotifyStop;
1873 // Detect interruption by DebugBreak() and force a switch to thread 0.
1874 if (exception.function == "ntdll!DbgBreakPoint")
1875 rc |= StopInArtificialThread;
1876 *message = msgInterrupted();
1880 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1881 *message = description;
1882 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1885 *message = msgStopped(QLatin1String(reason));
1886 rc |= StopReportStatusMessage|StopNotifyStop;
1890 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1896 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1897 elapsedLogTime(), messageBA.constData(),
1898 stateName(state()), m_specialStopMode);
1900 // Switch source level debugging
1901 syncOperateByInstruction(m_operateByInstructionPending);
1903 // Engine-special stop reasons: Breakpoints and setup
1904 const SpecialStopMode specialStopMode = m_specialStopMode;
1906 m_specialStopMode = NoSpecialStop;
1908 switch(specialStopMode) {
1909 case SpecialStopSynchronizeBreakpoints:
1911 qDebug("attemptBreakpointSynchronization in special stop");
1912 attemptBreakpointSynchronization();
1913 doContinueInferior();
1915 case SpecialStopGetWidgetAt:
1916 postWidgetAtCommand();
1918 case CustomSpecialStop:
1919 foreach (const QVariant &data, m_customSpecialStopData)
1920 handleCustomSpecialStop(data);
1921 m_customSpecialStopData.clear();
1922 doContinueInferior();
1928 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1929 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1930 notifyEngineSetupOk();
1934 stopReason.fromString(messageBA);
1935 processStop(stopReason, false);
1938 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1940 // Further examine stop and report to user
1942 QString exceptionBoxMessage;
1943 int forcedThreadId = -1;
1944 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1945 conditionalBreakPointTriggered);
1946 // Do the non-blocking log reporting
1947 if (stopFlags & StopReportLog)
1948 showMessage(message, LogMisc);
1949 if (stopFlags & StopReportStatusMessage)
1950 showStatusMessage(message);
1951 if (stopFlags & StopReportParseError)
1952 showMessage(message, LogError);
1953 // Ignore things like WOW64, report tracepoints.
1954 if (stopFlags & StopIgnoreContinue) {
1955 postCommand("g", 0);
1958 // Notify about state and send off command sequence to get stack, etc.
1959 if (stopFlags & StopNotifyStop) {
1960 if (state() == InferiorStopRequested) {
1961 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1962 notifyInferiorStopOk();
1964 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1965 notifyInferiorSpontaneousStop();
1967 // Prevent further commands from being sent if shutdown is in progress
1968 if (stopFlags & StopShutdownInProgress) {
1969 showMessage(QString::fromLatin1("Shutdown request detected..."));
1972 const bool sourceStepInto = m_sourceStepInto;
1973 m_sourceStepInto = false;
1974 // Start sequence to get all relevant data.
1975 if (stopFlags & StopInArtificialThread) {
1976 showMessage(tr("Switching to main thread..."), LogMisc);
1977 postCommand("~0 s", 0);
1979 // Re-fetch stack again.
1980 postCommandSequence(CommandListStack);
1982 const GdbMi stack = stopReason.findChild("stack");
1983 if (stack.isValid()) {
1984 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1985 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1989 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1992 const GdbMi threads = stopReason.findChild("threads");
1993 if (threads.isValid()) {
1994 parseThreads(threads, forcedThreadId);
1996 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1998 // Fire off remaining commands asynchronously
1999 if (!m_pendingBreakpointMap.isEmpty())
2000 postCommandSequence(CommandListBreakPoints);
2001 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
2002 postCommandSequence(CommandListRegisters);
2003 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2004 postCommandSequence(CommandListModules);
2006 // After the sequence has been sent off and CDB is pondering the commands,
2007 // pop up a message box for exceptions.
2008 if (stopFlags & StopShowExceptionMessageBox)
2009 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2012 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2014 const DebuggerState s = state();
2015 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2019 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2020 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2023 case EngineShutdownRequested:
2026 case InferiorShutdownRequested:
2034 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2036 const DebuggerState s = state();
2039 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2043 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2044 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2047 case EngineSetupRequested:
2049 case EngineRunRequested:
2050 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2051 notifyEngineRunAndInferiorRunOk();
2054 case InferiorStopOk:
2055 // Inaccessible without debuggee (exit breakpoint)
2056 // We go for spontaneous engine shutdown instead.
2057 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2059 qDebug("Lost debuggeee");
2060 m_hasDebuggee = false;
2063 case InferiorRunRequested:
2064 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2065 notifyInferiorRunOk();
2068 case EngineShutdownRequested:
2075 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2078 QDebug nospace = qDebug().nospace();
2079 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2080 << ' ' << stateName(state());
2081 if (t == 'N' || debug > 1) {
2082 nospace << ' ' << message;
2084 nospace << ' ' << message.size() << " bytes";
2088 // Is there a reply expected, some command queued?
2089 if (t == 'R' || t == 'N') {
2090 if (token == -1) { // Default token, user typed in extension command
2091 showMessage(QString::fromLatin1(message), LogMisc);
2094 const int index = indexOfCommand(m_extensionCommandQueue, token);
2096 // Did the command finish? Take off queue and complete, invoke CB
2097 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2099 command->success = true;
2100 command->reply = message;
2102 command->success = false;
2103 command->errorMessage = message;
2106 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2107 command->command.constData(), command->token, m_extensionCommandQueue.size());
2108 if (command->handler)
2109 (this->*(command->handler))(command);
2114 if (what == "debuggee_output") {
2115 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2119 if (what == "event") {
2120 showStatusMessage(QString::fromAscii(message), 5000);
2124 if (what == "session_accessible") {
2125 if (!m_accessible) {
2126 m_accessible = true;
2127 handleSessionAccessible(message.toULong());
2132 if (what == "session_inaccessible") {
2134 m_accessible = false;
2135 handleSessionInaccessible(message.toULong());
2140 if (what == "session_idle") {
2141 handleSessionIdle(message);
2145 if (what == "exception") {
2146 WinException exception;
2148 gdbmi.fromString(message);
2149 exception.fromGdbMI(gdbmi);
2150 const QString message = exception.toString(true);
2151 showStatusMessage(message);
2152 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2153 if (exception.exceptionCode == winExceptionCppException)
2154 showMessage(message + QLatin1Char('\n'), AppOutput);
2162 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2163 enum { CdbPromptLength = 7 };
2165 static inline bool isCdbPrompt(const QByteArray &c)
2167 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2168 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2169 && std::isdigit(c.at(4));
2172 // Check for '<token>32>' or '<token>32<'
2173 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2174 int *token, bool *isStart)
2178 const int tokenPrefixSize = tokenPrefix.size();
2179 const int size = c.size();
2180 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2182 switch (c.at(size - 1)) {
2192 if (!c.startsWith(tokenPrefix))
2195 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2199 void CdbEngine::parseOutputLine(QByteArray line)
2201 // The hooked output callback in the extension suppresses prompts,
2202 // it should happen only in initial and exit stages. Note however that
2203 // if the output is not hooked, sequences of prompts are possible which
2204 // can mix things up.
2205 while (isCdbPrompt(line))
2206 line.remove(0, CdbPromptLength);
2207 // An extension notification (potentially consisting of several chunks)
2208 if (line.startsWith(m_creatorExtPrefix)) {
2209 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2210 const char type = line.at(m_creatorExtPrefix.size());
2212 const int tokenPos = m_creatorExtPrefix.size() + 2;
2213 const int tokenEndPos = line.indexOf('|', tokenPos);
2214 QTC_ASSERT(tokenEndPos != -1, return)
2215 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2217 const int remainingChunksPos = tokenEndPos + 1;
2218 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2219 QTC_ASSERT(remainingChunksEndPos != -1, return)
2220 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2221 // const char 'serviceName'
2222 const int whatPos = remainingChunksEndPos + 1;
2223 const int whatEndPos = line.indexOf('|', whatPos);
2224 QTC_ASSERT(whatEndPos != -1, return)
2225 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2226 // Build up buffer, call handler once last chunk was encountered
2227 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2228 if (remainingChunks == 0) {
2229 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2230 m_extensionMessageBuffer.clear();
2234 // Check for command start/end tokens within which the builtin command
2235 // output is enclosed
2237 bool isStartToken = false;
2238 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2240 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2241 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2243 // If there is a current command, wait for end of output indicated by token,
2244 // command, trigger handler and finish, else append to its output.
2245 if (m_currentBuiltinCommandIndex != -1) {
2246 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2247 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2248 if (isCommandToken) {
2249 // Did the command finish? Invoke callback and remove from queue.
2251 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2252 currentCommand->command.constData(), currentCommand->token,
2253 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2254 QTC_ASSERT(token == currentCommand->token, return; );
2255 if (currentCommand->handler)
2256 (this->*(currentCommand->handler))(currentCommand);
2257 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2258 m_currentBuiltinCommandIndex = -1;
2260 // Record output of current command
2261 currentCommand->reply.push_back(line);
2264 } // m_currentCommandIndex
2265 if (isCommandToken) {
2266 // Beginning command token encountered, start to record output.
2267 const int index = indexOfCommand(m_builtinCommandQueue, token);
2268 QTC_ASSERT(isStartToken && index != -1, return; );
2269 m_currentBuiltinCommandIndex = index;
2270 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2272 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2276 showMessage(QString::fromLocal8Bit(line), LogMisc);
2279 void CdbEngine::readyReadStandardOut()
2281 if (m_ignoreCdbOutput)
2283 m_outputBuffer += m_process.readAllStandardOutput();
2284 // Split into lines and parse line by line.
2286 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2287 if (endOfLinePos == -1) {
2291 QByteArray line = m_outputBuffer.left(endOfLinePos);
2292 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2293 line.truncate(line.size() - 1);
2294 parseOutputLine(line);
2295 m_outputBuffer.remove(0, endOfLinePos + 1);
2300 void CdbEngine::readyReadStandardError()
2302 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2305 void CdbEngine::processError()
2307 showMessage(m_process.errorString(), LogError);
2311 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2312 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2314 QByteArray cmd(cmdC);
2315 ByteArrayInputStream str(cmd);
2316 foreach(const BreakpointData *bp, bps)
2317 str << ' ' << bp->bpNumber;
2322 bool CdbEngine::stateAcceptsBreakpointChanges() const
2326 case InferiorStopOk:
2334 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2336 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2337 if (!DebuggerEngine::isCppBreakpoint(data))
2339 switch (data.type) {
2341 case BreakpointAtFork:
2342 //case BreakpointAtVFork:
2343 case BreakpointAtSysCall:
2346 case BreakpointByFileAndLine:
2347 case BreakpointByFunction:
2348 case BreakpointByAddress:
2349 case BreakpointAtThrow:
2350 case BreakpointAtCatch:
2351 case BreakpointAtMain:
2352 case BreakpointAtExec:
2358 void CdbEngine::attemptBreakpointSynchronization()
2361 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2362 // Check if there is anything to be done at all.
2363 BreakHandler *handler = breakHandler();
2364 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2365 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2366 if (acceptsBreakpoint(id))
2367 handler->setEngine(id, this);
2369 // Quick check: is there a need to change something? - Populate module cache
2370 bool changed = false;
2371 const BreakpointIds ids = handler->engineBreakpointIds(this);
2372 foreach (BreakpointId id, ids) {
2373 switch (handler->state(id)) {
2374 case BreakpointInsertRequested:
2375 case BreakpointRemoveRequested:
2376 case BreakpointChangeRequested:
2379 case BreakpointInserted: {
2380 // Collect the new modules matching the files.
2381 // In the future, that information should be obtained from the build system.
2382 const BreakpointParameters &data = handler->breakpointData(id);
2383 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2384 m_fileNameModuleHash.insert(data.fileName, data.module);
2392 if (debugBreakpoints)
2393 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2394 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2398 if (!m_accessible) {
2400 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2401 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2404 // Add/Change breakpoints and store pending ones in map, since
2405 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2406 // handleBreakPoints will the complete that information and set it on the break handler.
2407 bool addedChanged = false;
2408 foreach (BreakpointId id, ids) {
2409 BreakpointParameters parameters = handler->breakpointData(id);
2410 BreakpointResponse response;
2411 response.fromParameters(parameters);
2412 // If we encountered that file and have a module for it: Add it.
2413 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2414 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2415 if (it != m_fileNameModuleHash.constEnd())
2416 parameters.module = it.value();
2418 switch (handler->state(id)) {
2419 case BreakpointInsertRequested:
2420 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2421 if (!parameters.enabled)
2422 postCommand("bd " + QByteArray::number(id), 0);
2423 handler->notifyBreakpointInsertProceeding(id);
2424 handler->notifyBreakpointInsertOk(id);
2425 m_pendingBreakpointMap.insert(id, response);
2426 addedChanged = true;
2427 // Ensure enabled/disabled is correct in handler and line number is there.
2428 handler->setResponse(id, response);
2429 if (debugBreakpoints)
2430 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2432 case BreakpointChangeRequested:
2433 handler->notifyBreakpointChangeProceeding(id);
2434 if (debugBreakpoints)
2435 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2436 qPrintable(parameters.toString()));
2437 if (parameters.enabled != handler->response(id).enabled) {
2438 // Change enabled/disabled breakpoints without triggering update.
2439 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2440 response.pending = false;
2441 response.enabled = parameters.enabled;
2442 handler->setResponse(id, response);
2444 // Delete and re-add, triggering update
2445 addedChanged = true;
2446 postCommand("bc " + QByteArray::number(id), 0);
2447 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2448 m_pendingBreakpointMap.insert(id, response);
2450 handler->notifyBreakpointChangeOk(id);
2452 case BreakpointRemoveRequested:
2453 postCommand("bc " + QByteArray::number(id), 0);
2454 handler->notifyBreakpointRemoveProceeding(id);
2455 handler->notifyBreakpointRemoveOk(id);
2456 m_pendingBreakpointMap.remove(id);
2462 // List breakpoints and send responses
2464 postCommandSequence(CommandListBreakPoints);
2467 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2468 // manager to correctly process it) and convert to clean path.
2469 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2472 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2473 if (it != m_normalizedFileCache.constEnd())
2475 if (debugSourceMapping)
2476 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2477 // Do we have source path mappings? ->Apply.
2478 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2480 // Up/lower case normalization according to Windows.
2482 QString normalized = winNormalizeFileName(fileName);
2484 QString normalized = fileName;
2486 if (debugSourceMapping)
2487 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2488 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2489 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2490 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2492 // At least upper case drive letter if failed.
2493 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2494 result.fileName[0] = result.fileName.at(0).toUpper();
2496 m_normalizedFileCache.insert(f, result);
2497 if (debugSourceMapping)
2498 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2502 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2503 // has more processing.
2504 static StackFrames parseFrames(const GdbMi &gdbmi)
2507 const int count = gdbmi.childCount();
2509 for (int i = 0; i < count; i++) {
2510 const GdbMi &frameMi = gdbmi.childAt(i);
2513 const GdbMi fullName = frameMi.findChild("fullname");
2514 if (fullName.isValid()) {
2515 frame.file = QFile::decodeName(fullName.data());
2516 frame.line = frameMi.findChild("line").data().toInt();
2517 frame.usable = false; // To be decided after source path mapping.
2519 frame.function = QLatin1String(frameMi.findChild("func").data());
2520 frame.from = QLatin1String(frameMi.findChild("from").data());
2521 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2522 rc.push_back(frame);
2527 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2529 // Parse frames, find current. Special handling for step into:
2530 // When stepping into on an actual function (source mode) by executing 't', an assembler
2531 // frame pointing at the jmp instruction is hit (noticeable by top function being
2532 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2533 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2535 StackFrames frames = parseFrames(data);
2536 const int count = frames.size();
2537 for (int i = 0; i < count; i++) {
2538 const bool hasFile = !frames.at(i).file.isEmpty();
2539 // jmp-frame hit by step into, do another 't' and abort sequence.
2540 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2541 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2542 return ParseStackStepInto;
2545 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2546 frames[i].file = fileName.fileName;
2547 frames[i].usable = fileName.exists;
2548 if (current == -1 && frames[i].usable)
2552 if (count && current == -1) // No usable frame, use assembly.
2555 stackHandler()->setFrames(frames);
2556 activateFrame(current);
2560 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2562 if (command->success) {
2564 data.fromString(command->reply);
2565 parseStackTrace(data, false);
2566 postCommandSequence(command->commandSequence);
2568 showMessage(command->errorMessage, LogError);
2572 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2575 if (command->success) {
2576 value = command->reply.toInt();
2578 showMessage(command->errorMessage, LogError);
2580 // Is this a conditional breakpoint?
2581 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2582 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2583 const QString message = value ?
2584 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2585 arg(value).arg(cookie.id) :
2586 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2588 showMessage(message, LogMisc);
2589 // Stop if evaluation is true, else continue
2591 processStop(cookie.stopReason, true);
2593 postCommand("g", 0);
2598 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2600 if (exp.contains(' ') && !exp.startsWith('"')) {
2604 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2607 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2609 postCommandSequence(command->commandSequence);
2612 // Post a sequence of standard commands: Trigger next once one completes successfully
2613 void CdbEngine::postCommandSequence(unsigned mask)
2616 qDebug("postCommandSequence 0x%x\n", mask);
2620 if (mask & CommandListThreads) {
2621 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2624 if (mask & CommandListStack) {
2625 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2628 if (mask & CommandListRegisters) {
2629 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2630 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2633 if (mask & CommandListModules) {
2634 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2637 if (mask & CommandListBreakPoints) {
2638 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2639 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2644 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2646 bool success = false;
2649 if (!reply->success) {
2650 message = QString::fromAscii(reply->errorMessage);
2653 // Should be "namespace::QWidget:0x555"
2654 QString watchExp = QString::fromAscii(reply->reply);
2655 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2657 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2660 // 0x000 -> nothing found
2661 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2662 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2665 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2666 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2667 watchExp.insert(0, QLatin1String("*("));
2668 watchHandler()->watchExpression(watchExp);
2672 showMessage(message, LogWarning);
2673 m_watchPointX = m_watchPointY = 0;
2676 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2679 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2683 str.setIntegerBase(16);
2684 str << ", at 0x" << r.address;
2685 str.setIntegerBase(10);
2688 str << ", disabled";
2689 if (!r.module.isEmpty())
2690 str << ", module: '" << r.module << '\'';
2694 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2696 if (debugBreakpoints)
2697 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2698 if (!reply->success) {
2699 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2703 value.fromString(reply->reply);
2704 if (value.type() != GdbMi::List) {
2705 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2708 handleBreakPoints(value);
2711 void CdbEngine::handleBreakPoints(const GdbMi &value)
2713 // Report all obtained parameters back. Note that not all parameters are reported
2714 // back, so, match by id and complete
2715 if (debugBreakpoints)
2716 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2718 QTextStream str(&message);
2719 BreakHandler *handler = breakHandler();
2720 foreach (const GdbMi &breakPointG, value.children()) {
2721 BreakpointResponse reportedResponse;
2722 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2723 if (debugBreakpoints)
2724 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2725 qPrintable(reportedResponse.toString()));
2727 if (!reportedResponse.pending) {
2728 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2729 if (it != m_pendingBreakpointMap.end()) {
2730 // Complete the response and set on handler.
2731 BreakpointResponse ¤tResponse = it.value();
2732 currentResponse.number = reportedResponse.number;
2733 currentResponse.address = reportedResponse.address;
2734 currentResponse.module = reportedResponse.module;
2735 currentResponse.pending = reportedResponse.pending;
2736 currentResponse.enabled = reportedResponse.enabled;
2737 formatCdbBreakPointResponse(id, currentResponse, str);
2738 if (debugBreakpoints)
2739 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2740 handler->setResponse(id, currentResponse);
2741 m_pendingBreakpointMap.erase(it);
2743 } // not pending reported
2745 if (m_pendingBreakpointMap.empty()) {
2746 str << QLatin1String("All breakpoints have been resolved.\n");
2748 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2750 showMessage(message, LogMisc);
2753 void CdbEngine::watchPoint(const QPoint &p)
2755 m_watchPointX = p.x();
2756 m_watchPointY = p.y();
2758 case InferiorStopOk:
2759 postWidgetAtCommand();
2762 // "Select Widget to Watch" from a running application is currently not
2763 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2764 // but requires some work as not to confuse the engine by state-change notifications
2765 // emitted by the debuggee function call.
2766 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2769 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2770 arg(QString::fromAscii(stateName(state()))), LogWarning);
2775 void CdbEngine::postWidgetAtCommand()
2777 QByteArray arguments = QByteArray::number(m_watchPointX);
2778 arguments.append(' ');
2779 arguments.append(QByteArray::number(m_watchPointY));
2780 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2783 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2785 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2786 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2787 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2790 if (qVariantCanConvert<MemoryViewCookie>(v)) {
2791 postFetchMemory(qVariantValue<MemoryViewCookie>(v));
2796 } // namespace Internal
2797 } // namespace Debugger