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 isCreatorConsole(const DebuggerStartParameters &sp, const CdbOptions &o)
205 return !o.cdbConsole && sp.useTerminal
206 && (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),
429 m_specialStopMode(NoSpecialStop),
430 m_nextCommandToken(0),
431 m_currentBuiltinCommandIndex(-1),
432 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
433 m_operateByInstructionPending(true),
434 m_operateByInstruction(true), // Default CDB setting
435 m_notifyEngineShutdownOnTermination(false),
436 m_hasDebuggee(false),
438 m_sourceStepInto(false),
441 m_ignoreCdbOutput(false)
443 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
445 setObjectName(QLatin1String("CdbEngine"));
446 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
447 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
448 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
449 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
452 void CdbEngine::init()
454 m_effectiveStartMode = NoStartMode;
455 notifyInferiorPid(0);
456 m_accessible = false;
457 m_specialStopMode = NoSpecialStop;
458 m_nextCommandToken = 0;
459 m_currentBuiltinCommandIndex = -1;
460 m_operateByInstructionPending = theAssemblerAction()->isChecked();
461 m_operateByInstruction = true; // Default CDB setting
462 m_notifyEngineShutdownOnTermination = false;
463 m_hasDebuggee = false;
464 m_sourceStepInto = false;
465 m_watchPointX = m_watchPointY = 0;
466 m_ignoreCdbOutput = false;
468 m_outputBuffer.clear();
469 m_builtinCommandQueue.clear();
470 m_extensionCommandQueue.clear();
471 m_extensionMessageBuffer.clear();
472 m_pendingBreakpointMap.clear();
473 m_customSpecialStopData.clear();
475 // Create local list of mappings in native separators
476 m_sourcePathMappings.clear();
477 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
478 if (!globalOptions->sourcePathMap.isEmpty()) {
479 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
480 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
481 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
482 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
483 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
484 QDir::toNativeSeparators(it.value())));
487 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
490 CdbEngine::~CdbEngine()
494 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
496 // To be set next time session becomes accessible
497 m_operateByInstructionPending = operateByInstruction;
498 if (state() == InferiorStopOk)
499 syncOperateByInstruction(operateByInstruction);
502 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
505 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
506 if (m_operateByInstruction == operateByInstruction)
508 QTC_ASSERT(m_accessible, return; )
509 m_operateByInstruction = operateByInstruction;
510 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
511 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
514 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
515 TextEditor::ITextEditor *editor,
516 const DebuggerToolTipContext &contextIn)
519 qDebug() << Q_FUNC_INFO;
520 // Need a stopped debuggee and a cpp file in a valid frame
521 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
523 // Determine expression and function
526 DebuggerToolTipContext context = contextIn;
527 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
528 // Are we in the current stack frame
529 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
531 // No numerical or any other expressions [yet]
532 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
534 // Can this be found as a local variable?
535 const QByteArray localsPrefix(localsPrefixC);
536 QByteArray iname = localsPrefix + exp.toAscii();
537 QModelIndex index = watchHandler()->itemIndex(iname);
538 if (!index.isValid()) {
539 // Nope, try a 'local.this.m_foo'.
540 exp.prepend(QLatin1String("this."));
541 iname.insert(localsPrefix.size(), "this.");
542 index = watchHandler()->itemIndex(iname);
543 if (!index.isValid())
546 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
547 tw->setContext(context);
548 tw->setDebuggerModel(LocalsWatch);
549 tw->setExpression(exp);
550 tw->acquireEngine(this);
551 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
555 // Determine full path to the CDB extension library.
556 QString CdbEngine::extensionLibraryName(bool is64Bit)
558 // Determine extension lib name and path to use
560 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
561 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
562 << '/' << QT_CREATOR_CDB_EXT << ".dll";
566 // Determine environment for CDB.exe, start out with run config and
567 // add CDB extension path merged with system value should there be one.
568 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
569 QString cdbExtensionPath)
571 // Determine CDB extension path from Qt Creator
572 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
573 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
574 if (!oldCdbExtensionPath.isEmpty()) {
575 cdbExtensionPath.append(QLatin1Char(';'));
576 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
578 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
579 // config, just to make sure, delete any existing entries
580 const QString cdbExtensionPathVariableAssign =
581 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
582 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
583 if (it->startsWith(cdbExtensionPathVariableAssign)) {
584 it = runConfigEnvironment.erase(it);
590 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
591 QDir::toNativeSeparators(cdbExtensionPath));
592 return runConfigEnvironment;
595 int CdbEngine::elapsedLogTime() const
597 const int elapsed = m_logTime.elapsed();
598 const int delta = elapsed - m_elapsedLogTime;
599 m_elapsedLogTime = elapsed;
603 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
604 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
607 qDebug("startConsole %s", qPrintable(sp.executable));
608 m_consoleStub.reset(new Utils::ConsoleProcess);
609 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
610 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
611 SLOT(consoleStubMessage(QString, bool)));
612 connect(m_consoleStub.data(), SIGNAL(processStarted()),
613 SLOT(consoleStubProcessStarted()));
614 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
615 SLOT(consoleStubExited()));
616 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
617 if (sp.environment.size())
618 m_consoleStub->setEnvironment(sp.environment);
619 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
620 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
626 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
629 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
631 if (state() == EngineSetupRequested) {
632 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
633 notifyEngineSetupFailed();
635 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
638 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
640 showMessage(msg, AppOutput);
644 void CdbEngine::consoleStubProcessStarted()
647 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
648 // Attach to console process.
649 DebuggerStartParameters attachParameters = startParameters();
650 attachParameters.executable.clear();
651 attachParameters.processArgs.clear();
652 attachParameters.attachPID = m_consoleStub->applicationPID();
653 attachParameters.startMode = AttachExternal;
654 attachParameters.useTerminal = false;
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 = isCreatorConsole(sp, *m_options);
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 (isCreatorConsole(startParameters(), *m_options))
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 && 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(inferiorPid()), LogMisc);
1154 if (!winDebugBreakProcess(inferiorPid(), &errorMessage)) {
1155 m_specialStopMode = oldSpecialMode;
1156 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(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 notifyInferiorPid(reply->reply.toULongLong());
1598 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1599 notifyInferiorSetupOk();
1601 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1602 arg(QLatin1String(reply->errorMessage)), LogError);
1603 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1604 notifyInferiorSetupFailed();
1608 // Parse CDB gdbmi register syntax
1609 static inline Register parseRegister(const GdbMi &gdbmiReg)
1612 reg.name = gdbmiReg.findChild("name").data();
1613 const GdbMi description = gdbmiReg.findChild("description");
1614 if (description.type() != GdbMi::Invalid) {
1616 reg.name += description.data();
1619 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1623 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1625 if (reply->success) {
1627 value.fromString(reply->reply);
1628 if (value.type() == GdbMi::List) {
1630 modules.reserve(value.childCount());
1631 foreach (const GdbMi &gdbmiModule, value.children()) {
1633 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1634 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1635 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1636 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1637 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1638 module.symbolsRead = Module::ReadOk;
1639 modules.push_back(module);
1641 modulesHandler()->setModules(modules);
1643 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1644 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1647 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1648 arg(QLatin1String(reply->errorMessage)), LogError);
1650 postCommandSequence(reply->commandSequence);
1654 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1656 if (reply->success) {
1658 value.fromString(reply->reply);
1659 if (value.type() == GdbMi::List) {
1660 Registers registers;
1661 registers.reserve(value.childCount());
1662 foreach (const GdbMi &gdbmiReg, value.children())
1663 registers.push_back(parseRegister(gdbmiReg));
1664 registerHandler()->setAndMarkRegisters(registers);
1666 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1667 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1670 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1671 arg(QLatin1String(reply->errorMessage)), LogError);
1673 postCommandSequence(reply->commandSequence);
1676 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1678 if (reply->success) {
1679 QList<WatchData> watchData;
1681 root.fromString(reply->reply);
1682 QTC_ASSERT(root.isList(), return ; )
1684 qDebug() << root.toString(true, 4);
1686 // Courtesy of GDB engine
1687 foreach (const GdbMi &child, root.children()) {
1689 dummy.iname = child.findChild("iname").data();
1690 dummy.name = QLatin1String(child.findChild("name").data());
1691 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1693 watchHandler()->insertBulkData(watchData);
1694 watchHandler()->endCycle();
1696 QDebug nsp = qDebug().nospace();
1697 nsp << "Obtained " << watchData.size() << " items:\n";
1698 foreach (const WatchData &wd, watchData)
1699 nsp << wd.toString() <<'\n';
1701 const bool forNewStackFrame = reply->cookie.toBool();
1702 if (forNewStackFrame)
1703 emit stackFrameCompleted();
1705 showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1709 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1711 if (!reply->success)
1712 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1715 enum CdbExecutionStatus {
1716 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1717 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1718 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1719 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1720 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1721 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1722 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1724 static const char *cdbStatusName(unsigned long s)
1727 case CDB_STATUS_NO_CHANGE:
1731 case CDB_STATUS_GO_HANDLED:
1732 return "go_handled";
1733 case CDB_STATUS_GO_NOT_HANDLED:
1734 return "go_not_handled";
1735 case CDB_STATUS_STEP_OVER:
1737 case CDB_STATUS_STEP_INTO:
1739 case CDB_STATUS_BREAK:
1741 case CDB_STATUS_NO_DEBUGGEE:
1742 return "no_debuggee";
1743 case CDB_STATUS_STEP_BRANCH:
1744 return "step_branch";
1745 case CDB_STATUS_IGNORE_EVENT:
1746 return "ignore_event";
1747 case CDB_STATUS_RESTART_REQUESTED:
1748 return "restart_requested";
1749 case CDB_STATUS_REVERSE_GO:
1750 return "reverse_go";
1751 case CDB_STATUS_REVERSE_STEP_BRANCH:
1752 return "reverse_step_branch";
1753 case CDB_STATUS_REVERSE_STEP_OVER:
1754 return "reverse_step_over";
1755 case CDB_STATUS_REVERSE_STEP_INTO:
1756 return "reverse_step_into";
1761 /* Examine how to react to a stop. */
1762 enum StopActionFlags
1765 StopReportLog = 0x1,
1766 StopReportStatusMessage = 0x2,
1767 StopReportParseError = 0x4,
1768 StopShowExceptionMessageBox = 0x8,
1769 // Notify stop or just continue
1770 StopNotifyStop = 0x10,
1771 StopIgnoreContinue = 0x20,
1772 // Hit on break in artificial stop thread (created by DebugBreak()).
1773 StopInArtificialThread = 0x40,
1774 StopShutdownInProgress = 0x80 // Shutdown in progress
1777 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1778 const QString &threadId)
1780 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1781 .arg(id).arg(number).arg(threadId);
1784 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1785 const QByteArray &condition,
1786 const QString &threadId)
1788 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1789 .arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
1792 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1794 QString *exceptionBoxMessage,
1795 bool conditionalBreakPointTriggered)
1797 // Report stop reason (GDBMI)
1799 if (targetState() == DebuggerFinished)
1800 rc |= StopShutdownInProgress;
1802 qDebug("%s", stopReason.toString(true, 4).constData());
1803 const QByteArray reason = stopReason.findChild("reason").data();
1804 if (reason.isEmpty()) {
1805 *message = tr("Malformed stop response received.");
1806 rc |= StopReportParseError|StopNotifyStop;
1809 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1810 if (state() == InferiorStopOk) {
1811 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1812 arg(QString::fromAscii(reason));
1813 rc |= StopReportLog;
1816 const int threadId = stopReason.findChild("threadId").data().toInt();
1817 if (reason == "breakpoint") {
1818 // Note: Internal breakpoints (run to line) are reported with id=0.
1819 // Step out creates temporary breakpoints with id 10000.
1820 BreakpointId id = 0;
1822 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1823 if (breakpointIdG.isValid()) {
1824 id = breakpointIdG.data().toULongLong();
1825 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1826 const BreakpointResponse parameters = breakHandler()->response(id);
1827 // Trace point? Just report.
1828 number = parameters.number;
1829 if (parameters.tracepoint) {
1830 *message = msgTracePointTriggered(id, number, QString::number(threadId));
1831 return StopReportLog|StopIgnoreContinue;
1833 // Trigger evaluation of BP expression unless we are already in the response.
1834 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1835 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1836 QString::number(threadId));
1837 ConditionalBreakPointCookie cookie(id);
1838 cookie.stopReason = stopReason;
1839 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1840 return StopReportLog;
1846 if (id && breakHandler()->type(id) == Watchpoint) {
1847 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1849 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1851 rc |= StopReportStatusMessage|StopNotifyStop;
1854 if (reason == "exception") {
1855 WinException exception;
1856 exception.fromGdbMI(stopReason);
1857 QString description = exception.toString();
1859 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1860 // pulls DLLs. Avoid showing a 'stopped' Message box.
1861 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1862 || exception.exceptionCode == winExceptionWX86Breakpoint)
1863 return StopNotifyStop;
1864 if (exception.exceptionCode == winExceptionCtrlPressed) {
1865 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1866 *message = msgInterrupted();
1867 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1870 if (isDebuggerWinException(exception.exceptionCode)) {
1871 rc |= StopReportStatusMessage|StopNotifyStop;
1872 // Detect interruption by DebugBreak() and force a switch to thread 0.
1873 if (exception.function == "ntdll!DbgBreakPoint")
1874 rc |= StopInArtificialThread;
1875 *message = msgInterrupted();
1879 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1880 *message = description;
1881 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1884 *message = msgStopped(QLatin1String(reason));
1885 rc |= StopReportStatusMessage|StopNotifyStop;
1889 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1895 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1896 elapsedLogTime(), messageBA.constData(),
1897 stateName(state()), m_specialStopMode);
1899 // Switch source level debugging
1900 syncOperateByInstruction(m_operateByInstructionPending);
1902 // Engine-special stop reasons: Breakpoints and setup
1903 const SpecialStopMode specialStopMode = m_specialStopMode;
1905 m_specialStopMode = NoSpecialStop;
1907 switch(specialStopMode) {
1908 case SpecialStopSynchronizeBreakpoints:
1910 qDebug("attemptBreakpointSynchronization in special stop");
1911 attemptBreakpointSynchronization();
1912 doContinueInferior();
1914 case SpecialStopGetWidgetAt:
1915 postWidgetAtCommand();
1917 case CustomSpecialStop:
1918 foreach (const QVariant &data, m_customSpecialStopData)
1919 handleCustomSpecialStop(data);
1920 m_customSpecialStopData.clear();
1921 doContinueInferior();
1927 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1928 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1929 notifyEngineSetupOk();
1933 stopReason.fromString(messageBA);
1934 processStop(stopReason, false);
1937 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1939 // Further examine stop and report to user
1941 QString exceptionBoxMessage;
1942 int forcedThreadId = -1;
1943 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1944 conditionalBreakPointTriggered);
1945 // Do the non-blocking log reporting
1946 if (stopFlags & StopReportLog)
1947 showMessage(message, LogMisc);
1948 if (stopFlags & StopReportStatusMessage)
1949 showStatusMessage(message);
1950 if (stopFlags & StopReportParseError)
1951 showMessage(message, LogError);
1952 // Ignore things like WOW64, report tracepoints.
1953 if (stopFlags & StopIgnoreContinue) {
1954 postCommand("g", 0);
1957 // Notify about state and send off command sequence to get stack, etc.
1958 if (stopFlags & StopNotifyStop) {
1959 if (state() == InferiorStopRequested) {
1960 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1961 notifyInferiorStopOk();
1963 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1964 notifyInferiorSpontaneousStop();
1966 // Prevent further commands from being sent if shutdown is in progress
1967 if (stopFlags & StopShutdownInProgress) {
1968 showMessage(QString::fromLatin1("Shutdown request detected..."));
1971 const bool sourceStepInto = m_sourceStepInto;
1972 m_sourceStepInto = false;
1973 // Start sequence to get all relevant data.
1974 if (stopFlags & StopInArtificialThread) {
1975 showMessage(tr("Switching to main thread..."), LogMisc);
1976 postCommand("~0 s", 0);
1978 // Re-fetch stack again.
1979 postCommandSequence(CommandListStack);
1981 const GdbMi stack = stopReason.findChild("stack");
1982 if (stack.isValid()) {
1983 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1984 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1988 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1991 const GdbMi threads = stopReason.findChild("threads");
1992 if (threads.isValid()) {
1993 parseThreads(threads, forcedThreadId);
1995 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1997 // Fire off remaining commands asynchronously
1998 if (!m_pendingBreakpointMap.isEmpty())
1999 postCommandSequence(CommandListBreakPoints);
2000 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
2001 postCommandSequence(CommandListRegisters);
2002 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2003 postCommandSequence(CommandListModules);
2005 // After the sequence has been sent off and CDB is pondering the commands,
2006 // pop up a message box for exceptions.
2007 if (stopFlags & StopShowExceptionMessageBox)
2008 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2011 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2013 const DebuggerState s = state();
2014 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2018 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2019 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2022 case EngineShutdownRequested:
2025 case InferiorShutdownRequested:
2033 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2035 const DebuggerState s = state();
2038 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2042 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2043 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2046 case EngineSetupRequested:
2048 case EngineRunRequested:
2049 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2050 notifyEngineRunAndInferiorRunOk();
2053 case InferiorStopOk:
2054 // Inaccessible without debuggee (exit breakpoint)
2055 // We go for spontaneous engine shutdown instead.
2056 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2058 qDebug("Lost debuggeee");
2059 m_hasDebuggee = false;
2062 case InferiorRunRequested:
2063 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2064 notifyInferiorRunOk();
2067 case EngineShutdownRequested:
2074 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2077 QDebug nospace = qDebug().nospace();
2078 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2079 << ' ' << stateName(state());
2080 if (t == 'N' || debug > 1) {
2081 nospace << ' ' << message;
2083 nospace << ' ' << message.size() << " bytes";
2087 // Is there a reply expected, some command queued?
2088 if (t == 'R' || t == 'N') {
2089 if (token == -1) { // Default token, user typed in extension command
2090 showMessage(QString::fromLatin1(message), LogMisc);
2093 const int index = indexOfCommand(m_extensionCommandQueue, token);
2095 // Did the command finish? Take off queue and complete, invoke CB
2096 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2098 command->success = true;
2099 command->reply = message;
2101 command->success = false;
2102 command->errorMessage = message;
2105 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2106 command->command.constData(), command->token, m_extensionCommandQueue.size());
2107 if (command->handler)
2108 (this->*(command->handler))(command);
2113 if (what == "debuggee_output") {
2114 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2118 if (what == "event") {
2119 showStatusMessage(QString::fromAscii(message), 5000);
2123 if (what == "session_accessible") {
2124 if (!m_accessible) {
2125 m_accessible = true;
2126 handleSessionAccessible(message.toULong());
2131 if (what == "session_inaccessible") {
2133 m_accessible = false;
2134 handleSessionInaccessible(message.toULong());
2139 if (what == "session_idle") {
2140 handleSessionIdle(message);
2144 if (what == "exception") {
2145 WinException exception;
2147 gdbmi.fromString(message);
2148 exception.fromGdbMI(gdbmi);
2149 const QString message = exception.toString(true);
2150 showStatusMessage(message);
2151 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2152 if (exception.exceptionCode == winExceptionCppException)
2153 showMessage(message + QLatin1Char('\n'), AppOutput);
2161 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2162 enum { CdbPromptLength = 7 };
2164 static inline bool isCdbPrompt(const QByteArray &c)
2166 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2167 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2168 && std::isdigit(c.at(4));
2171 // Check for '<token>32>' or '<token>32<'
2172 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2173 int *token, bool *isStart)
2177 const int tokenPrefixSize = tokenPrefix.size();
2178 const int size = c.size();
2179 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2181 switch (c.at(size - 1)) {
2191 if (!c.startsWith(tokenPrefix))
2194 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2198 void CdbEngine::parseOutputLine(QByteArray line)
2200 // The hooked output callback in the extension suppresses prompts,
2201 // it should happen only in initial and exit stages. Note however that
2202 // if the output is not hooked, sequences of prompts are possible which
2203 // can mix things up.
2204 while (isCdbPrompt(line))
2205 line.remove(0, CdbPromptLength);
2206 // An extension notification (potentially consisting of several chunks)
2207 if (line.startsWith(m_creatorExtPrefix)) {
2208 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2209 const char type = line.at(m_creatorExtPrefix.size());
2211 const int tokenPos = m_creatorExtPrefix.size() + 2;
2212 const int tokenEndPos = line.indexOf('|', tokenPos);
2213 QTC_ASSERT(tokenEndPos != -1, return)
2214 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2216 const int remainingChunksPos = tokenEndPos + 1;
2217 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2218 QTC_ASSERT(remainingChunksEndPos != -1, return)
2219 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2220 // const char 'serviceName'
2221 const int whatPos = remainingChunksEndPos + 1;
2222 const int whatEndPos = line.indexOf('|', whatPos);
2223 QTC_ASSERT(whatEndPos != -1, return)
2224 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2225 // Build up buffer, call handler once last chunk was encountered
2226 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2227 if (remainingChunks == 0) {
2228 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2229 m_extensionMessageBuffer.clear();
2233 // Check for command start/end tokens within which the builtin command
2234 // output is enclosed
2236 bool isStartToken = false;
2237 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2239 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2240 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2242 // If there is a current command, wait for end of output indicated by token,
2243 // command, trigger handler and finish, else append to its output.
2244 if (m_currentBuiltinCommandIndex != -1) {
2245 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2246 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2247 if (isCommandToken) {
2248 // Did the command finish? Invoke callback and remove from queue.
2250 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2251 currentCommand->command.constData(), currentCommand->token,
2252 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2253 QTC_ASSERT(token == currentCommand->token, return; );
2254 if (currentCommand->handler)
2255 (this->*(currentCommand->handler))(currentCommand);
2256 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2257 m_currentBuiltinCommandIndex = -1;
2259 // Record output of current command
2260 currentCommand->reply.push_back(line);
2263 } // m_currentCommandIndex
2264 if (isCommandToken) {
2265 // Beginning command token encountered, start to record output.
2266 const int index = indexOfCommand(m_builtinCommandQueue, token);
2267 QTC_ASSERT(isStartToken && index != -1, return; );
2268 m_currentBuiltinCommandIndex = index;
2269 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2271 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2275 showMessage(QString::fromLocal8Bit(line), LogMisc);
2278 void CdbEngine::readyReadStandardOut()
2280 if (m_ignoreCdbOutput)
2282 m_outputBuffer += m_process.readAllStandardOutput();
2283 // Split into lines and parse line by line.
2285 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2286 if (endOfLinePos == -1) {
2290 QByteArray line = m_outputBuffer.left(endOfLinePos);
2291 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2292 line.truncate(line.size() - 1);
2293 parseOutputLine(line);
2294 m_outputBuffer.remove(0, endOfLinePos + 1);
2299 void CdbEngine::readyReadStandardError()
2301 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2304 void CdbEngine::processError()
2306 showMessage(m_process.errorString(), LogError);
2310 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2311 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2313 QByteArray cmd(cmdC);
2314 ByteArrayInputStream str(cmd);
2315 foreach(const BreakpointData *bp, bps)
2316 str << ' ' << bp->bpNumber;
2321 bool CdbEngine::stateAcceptsBreakpointChanges() const
2325 case InferiorStopOk:
2333 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2335 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2336 if (!DebuggerEngine::isCppBreakpoint(data))
2338 switch (data.type) {
2340 case BreakpointAtFork:
2341 //case BreakpointAtVFork:
2342 case BreakpointAtSysCall:
2345 case BreakpointByFileAndLine:
2346 case BreakpointByFunction:
2347 case BreakpointByAddress:
2348 case BreakpointAtThrow:
2349 case BreakpointAtCatch:
2350 case BreakpointAtMain:
2351 case BreakpointAtExec:
2357 void CdbEngine::attemptBreakpointSynchronization()
2360 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2361 // Check if there is anything to be done at all.
2362 BreakHandler *handler = breakHandler();
2363 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2364 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2365 if (acceptsBreakpoint(id))
2366 handler->setEngine(id, this);
2368 // Quick check: is there a need to change something? - Populate module cache
2369 bool changed = false;
2370 const BreakpointIds ids = handler->engineBreakpointIds(this);
2371 foreach (BreakpointId id, ids) {
2372 switch (handler->state(id)) {
2373 case BreakpointInsertRequested:
2374 case BreakpointRemoveRequested:
2375 case BreakpointChangeRequested:
2378 case BreakpointInserted: {
2379 // Collect the new modules matching the files.
2380 // In the future, that information should be obtained from the build system.
2381 const BreakpointParameters &data = handler->breakpointData(id);
2382 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2383 m_fileNameModuleHash.insert(data.fileName, data.module);
2391 if (debugBreakpoints)
2392 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2393 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2397 if (!m_accessible) {
2399 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2400 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2403 // Add/Change breakpoints and store pending ones in map, since
2404 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2405 // handleBreakPoints will the complete that information and set it on the break handler.
2406 bool addedChanged = false;
2407 foreach (BreakpointId id, ids) {
2408 BreakpointParameters parameters = handler->breakpointData(id);
2409 BreakpointResponse response;
2410 response.fromParameters(parameters);
2411 // If we encountered that file and have a module for it: Add it.
2412 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2413 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2414 if (it != m_fileNameModuleHash.constEnd())
2415 parameters.module = it.value();
2417 switch (handler->state(id)) {
2418 case BreakpointInsertRequested:
2419 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2420 if (!parameters.enabled)
2421 postCommand("bd " + QByteArray::number(id), 0);
2422 handler->notifyBreakpointInsertProceeding(id);
2423 handler->notifyBreakpointInsertOk(id);
2424 m_pendingBreakpointMap.insert(id, response);
2425 addedChanged = true;
2426 // Ensure enabled/disabled is correct in handler and line number is there.
2427 handler->setResponse(id, response);
2428 if (debugBreakpoints)
2429 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2431 case BreakpointChangeRequested:
2432 handler->notifyBreakpointChangeProceeding(id);
2433 if (debugBreakpoints)
2434 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2435 qPrintable(parameters.toString()));
2436 if (parameters.enabled != handler->response(id).enabled) {
2437 // Change enabled/disabled breakpoints without triggering update.
2438 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2439 response.pending = false;
2440 response.enabled = parameters.enabled;
2441 handler->setResponse(id, response);
2443 // Delete and re-add, triggering update
2444 addedChanged = true;
2445 postCommand("bc " + QByteArray::number(id), 0);
2446 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2447 m_pendingBreakpointMap.insert(id, response);
2449 handler->notifyBreakpointChangeOk(id);
2451 case BreakpointRemoveRequested:
2452 postCommand("bc " + QByteArray::number(id), 0);
2453 handler->notifyBreakpointRemoveProceeding(id);
2454 handler->notifyBreakpointRemoveOk(id);
2455 m_pendingBreakpointMap.remove(id);
2461 // List breakpoints and send responses
2463 postCommandSequence(CommandListBreakPoints);
2466 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2467 // manager to correctly process it) and convert to clean path.
2468 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2471 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2472 if (it != m_normalizedFileCache.constEnd())
2474 if (debugSourceMapping)
2475 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2476 // Do we have source path mappings? ->Apply.
2477 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2479 // Up/lower case normalization according to Windows.
2481 QString normalized = Utils::normalizePathName(fileName);
2483 QString normalized = fileName;
2485 if (debugSourceMapping)
2486 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2487 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2488 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2489 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2491 // At least upper case drive letter if failed.
2492 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2493 result.fileName[0] = result.fileName.at(0).toUpper();
2495 m_normalizedFileCache.insert(f, result);
2496 if (debugSourceMapping)
2497 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2501 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2502 // has more processing.
2503 static StackFrames parseFrames(const GdbMi &gdbmi)
2506 const int count = gdbmi.childCount();
2508 for (int i = 0; i < count; i++) {
2509 const GdbMi &frameMi = gdbmi.childAt(i);
2512 const GdbMi fullName = frameMi.findChild("fullname");
2513 if (fullName.isValid()) {
2514 frame.file = QFile::decodeName(fullName.data());
2515 frame.line = frameMi.findChild("line").data().toInt();
2516 frame.usable = false; // To be decided after source path mapping.
2518 frame.function = QLatin1String(frameMi.findChild("func").data());
2519 frame.from = QLatin1String(frameMi.findChild("from").data());
2520 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2521 rc.push_back(frame);
2526 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2528 // Parse frames, find current. Special handling for step into:
2529 // When stepping into on an actual function (source mode) by executing 't', an assembler
2530 // frame pointing at the jmp instruction is hit (noticeable by top function being
2531 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2532 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2534 StackFrames frames = parseFrames(data);
2535 const int count = frames.size();
2536 for (int i = 0; i < count; i++) {
2537 const bool hasFile = !frames.at(i).file.isEmpty();
2538 // jmp-frame hit by step into, do another 't' and abort sequence.
2539 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2540 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2541 return ParseStackStepInto;
2544 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2545 frames[i].file = fileName.fileName;
2546 frames[i].usable = fileName.exists;
2547 if (current == -1 && frames[i].usable)
2551 if (count && current == -1) // No usable frame, use assembly.
2554 stackHandler()->setFrames(frames);
2555 activateFrame(current);
2559 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2561 if (command->success) {
2563 data.fromString(command->reply);
2564 parseStackTrace(data, false);
2565 postCommandSequence(command->commandSequence);
2567 showMessage(command->errorMessage, LogError);
2571 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2574 if (command->success) {
2575 value = command->reply.toInt();
2577 showMessage(command->errorMessage, LogError);
2579 // Is this a conditional breakpoint?
2580 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2581 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2582 const QString message = value ?
2583 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2584 arg(value).arg(cookie.id) :
2585 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2587 showMessage(message, LogMisc);
2588 // Stop if evaluation is true, else continue
2590 processStop(cookie.stopReason, true);
2592 postCommand("g", 0);
2597 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2599 if (exp.contains(' ') && !exp.startsWith('"')) {
2603 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2606 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2608 postCommandSequence(command->commandSequence);
2611 // Post a sequence of standard commands: Trigger next once one completes successfully
2612 void CdbEngine::postCommandSequence(unsigned mask)
2615 qDebug("postCommandSequence 0x%x\n", mask);
2619 if (mask & CommandListThreads) {
2620 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2623 if (mask & CommandListStack) {
2624 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2627 if (mask & CommandListRegisters) {
2628 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2629 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2632 if (mask & CommandListModules) {
2633 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2636 if (mask & CommandListBreakPoints) {
2637 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2638 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2643 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2645 bool success = false;
2648 if (!reply->success) {
2649 message = QString::fromAscii(reply->errorMessage);
2652 // Should be "namespace::QWidget:0x555"
2653 QString watchExp = QString::fromAscii(reply->reply);
2654 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2656 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2659 // 0x000 -> nothing found
2660 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2661 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2664 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2665 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2666 watchExp.insert(0, QLatin1String("*("));
2667 watchHandler()->watchExpression(watchExp);
2671 showMessage(message, LogWarning);
2672 m_watchPointX = m_watchPointY = 0;
2675 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2678 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2682 str.setIntegerBase(16);
2683 str << ", at 0x" << r.address;
2684 str.setIntegerBase(10);
2687 str << ", disabled";
2688 if (!r.module.isEmpty())
2689 str << ", module: '" << r.module << '\'';
2693 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2695 if (debugBreakpoints)
2696 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2697 if (!reply->success) {
2698 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2702 value.fromString(reply->reply);
2703 if (value.type() != GdbMi::List) {
2704 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2707 handleBreakPoints(value);
2710 void CdbEngine::handleBreakPoints(const GdbMi &value)
2712 // Report all obtained parameters back. Note that not all parameters are reported
2713 // back, so, match by id and complete
2714 if (debugBreakpoints)
2715 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2717 QTextStream str(&message);
2718 BreakHandler *handler = breakHandler();
2719 foreach (const GdbMi &breakPointG, value.children()) {
2720 BreakpointResponse reportedResponse;
2721 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2722 if (debugBreakpoints)
2723 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2724 qPrintable(reportedResponse.toString()));
2726 if (!reportedResponse.pending) {
2727 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2728 if (it != m_pendingBreakpointMap.end()) {
2729 // Complete the response and set on handler.
2730 BreakpointResponse ¤tResponse = it.value();
2731 currentResponse.number = reportedResponse.number;
2732 currentResponse.address = reportedResponse.address;
2733 currentResponse.module = reportedResponse.module;
2734 currentResponse.pending = reportedResponse.pending;
2735 currentResponse.enabled = reportedResponse.enabled;
2736 formatCdbBreakPointResponse(id, currentResponse, str);
2737 if (debugBreakpoints)
2738 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2739 handler->setResponse(id, currentResponse);
2740 m_pendingBreakpointMap.erase(it);
2742 } // not pending reported
2744 if (m_pendingBreakpointMap.empty()) {
2745 str << QLatin1String("All breakpoints have been resolved.\n");
2747 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2749 showMessage(message, LogMisc);
2752 void CdbEngine::watchPoint(const QPoint &p)
2754 m_watchPointX = p.x();
2755 m_watchPointY = p.y();
2757 case InferiorStopOk:
2758 postWidgetAtCommand();
2761 // "Select Widget to Watch" from a running application is currently not
2762 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2763 // but requires some work as not to confuse the engine by state-change notifications
2764 // emitted by the debuggee function call.
2765 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2768 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2769 arg(QString::fromAscii(stateName(state()))), LogWarning);
2774 void CdbEngine::postWidgetAtCommand()
2776 QByteArray arguments = QByteArray::number(m_watchPointX);
2777 arguments.append(' ');
2778 arguments.append(QByteArray::number(m_watchPointY));
2779 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2782 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2784 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2785 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2786 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2789 if (qVariantCanConvert<MemoryViewCookie>(v)) {
2790 postFetchMemory(qVariantValue<MemoryViewCookie>(v));
2795 } // namespace Internal
2796 } // namespace Debugger