1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "cdbengine.h"
35 #include "debuggerstartparameters.h"
36 #include "disassemblerlines.h"
37 #include "cdboptions.h"
38 #include "cdboptionspage.h"
39 #include "bytearrayinputstream.h"
40 #include "breakpoint.h"
41 #include "breakhandler.h"
42 #include "stackframe.h"
43 #include "stackhandler.h"
44 #include "watchhandler.h"
45 #include "threadshandler.h"
46 #include "moduleshandler.h"
47 #include "debuggeractions.h"
48 #include "debuggercore.h"
49 #include "registerhandler.h"
50 #include "disassembleragent.h"
51 #include "memoryagent.h"
52 #include "debuggerrunner.h"
53 #include "debuggertooltipmanager.h"
54 #include "cdbparsehelpers.h"
55 #include "watchutils.h"
56 #include "gdb/gdbmi.h"
57 #include "shared/cdbsymbolpathlisteditor.h"
59 #include <coreplugin/icore.h>
60 #include <texteditor/itexteditor.h>
61 #include <projectexplorer/abi.h>
62 #include <projectexplorer/projectexplorerconstants.h>
64 #include <utils/synchronousprocess.h>
65 #include <utils/winutils.h>
66 #include <utils/qtcassert.h>
67 #include <utils/savedaction.h>
68 #include <utils/consoleprocess.h>
70 #include <QtCore/QCoreApplication>
71 #include <QtCore/QFileInfo>
72 #include <QtCore/QDir>
73 #include <QtCore/QDebug>
74 #include <QtCore/QTextStream>
75 #include <QtCore/QDateTime>
76 #include <QtGui/QToolTip>
77 #include <QtGui/QMainWindow>
78 #include <QtGui/QMessageBox>
81 # include <utils/winutils.h>
82 # include "dbgwinutils.h"
87 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
88 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
91 enum { debugLocals = 0 };
92 enum { debugSourceMapping = 0 };
93 enum { debugWatches = 0 };
94 enum { debugBreakpoints = 0 };
97 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
99 # define STATE_DEBUG(state, func, line, notifyFunc)
103 \class Debugger::Internal::CdbEngine
105 Cdb engine version 2: Run the CDB process on pipes and parse its output.
106 The engine relies on a CDB extension Qt Creator provides as an extension
107 library (32/64bit), which is loaded into cdb.exe. It serves to:
110 \o Notify the engine about the state of the debugging session:
112 \o idle: (hooked up with .idle_cmd) debuggee stopped
113 \o accessible: Debuggee stopped, cdb.exe accepts commands
114 \o inaccessible: Debuggee runs, no way to post commands
115 \o session active/inactive: Lost debuggee, terminating.
117 \o Hook up with output/event callbacks and produce formatted output to be able
118 to catch application output and exceptions.
119 \o Provide some extension commands that produce output in a standardized (GDBMI)
120 format that ends up in handleExtensionMessage(), for example:
122 \o pid Return debuggee pid for interrupting.
123 \o locals Print locals from SymbolGroup
124 \o expandLocals Expand locals in symbol group
125 \o registers, modules, threads
129 Debugger commands can be posted by calling:
133 \o postCommand(): Does not expect a reply
134 \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
135 that is captured by enclosing it in special tokens using the 'echo' command and
136 then invokes a callback with a CdbBuiltinCommand structure.
137 \o postExtensionCommand(): Run a command provided by the extension producing
138 one-line output and invoke a callback with a CdbExtensionCommand structure
139 (output is potentially split up in chunks).
144 [Console: The console stub launches the process. On process startup,
145 launchCDB() is called with AttachExternal].
146 setupEngine() calls launchCDB() with the startparameters. The debuggee
147 runs into the initial breakpoint (session idle). EngineSetupOk is
148 notified (inferior still stopped). setupInferior() is then called
149 which does breakpoint synchronization and issues the extension 'pid'
150 command to obtain the inferior pid (which also hooks up the output callbacks).
151 handlePid() notifies notifyInferiorSetupOk.
152 runEngine() is then called which issues 'g' to continue the inferior.
153 Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
154 when the inferior exits (except attach modes).
157 using namespace ProjectExplorer;
162 static const char localsPrefixC[] = "local.";
164 struct MemoryViewCookie
166 explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
167 quint64 addr = 0, quint64 l = 0) :
168 agent(a), editorToken(e), address(addr), length(l)
172 QObject *editorToken;
177 struct MemoryChangeCookie
179 explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
180 address(addr), data(d) {}
186 struct ConditionalBreakPointCookie
188 ConditionalBreakPointCookie(BreakpointId i = 0) : id(i) {}
193 } // namespace Internal
194 } // namespace Debugger
196 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
197 Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
198 Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
203 static inline bool isConsole(const DebuggerStartParameters &sp)
205 return (sp.startMode == StartInternal || sp.startMode == StartExternal)
210 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
212 QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
213 debuggerCore()->mainWindow());
214 mb->setAttribute(Qt::WA_DeleteOnClose);
219 // Base data structure for command queue entries with callback
220 struct CdbCommandBase
222 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
225 CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
226 unsigned nc, const QVariant &cookie);
232 // Continue with another commands as specified in CommandSequenceFlags
233 unsigned commandSequence;
236 CdbCommandBase::CdbCommandBase() :
237 token(0), flags(0), commandSequence(0)
241 CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
242 unsigned nc, const QVariant &c) :
243 token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
247 // Queue entry for builtin commands producing free-format
248 // line-by-line output.
249 struct CdbBuiltinCommand : public CdbCommandBase
251 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
253 CdbBuiltinCommand() {}
254 CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
256 unsigned nc, const QVariant &cookie) :
257 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
261 QByteArray joinedReply() const;
263 CommandHandler handler;
264 QList<QByteArray> reply;
267 QByteArray CdbBuiltinCommand::joinedReply() const
272 answer.reserve(120 * reply.size());
273 foreach (const QByteArray &l, reply) {
280 // Queue entry for Qt Creator extension commands producing one-line
281 // output with success flag and error message.
282 struct CdbExtensionCommand : public CdbCommandBase
284 typedef CdbEngine::ExtensionCommandHandler CommandHandler;
286 CdbExtensionCommand() : success(false) {}
287 CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
289 unsigned nc, const QVariant &cookie) :
290 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
292 CommandHandler handler;
294 QByteArray errorMessage;
298 template <class CommandPtrType>
299 int indexOfCommand(const QList<CommandPtrType> &l, int token)
301 const int count = l.size();
302 for (int i = 0; i < count; i++)
303 if (l.at(i)->token == token)
308 static inline bool validMode(DebuggerStartMode sm)
322 // Accessed by RunControlFactory
323 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
324 DebuggerEngine *masterEngine, QString *errorMessage)
327 CdbOptionsPage *op = CdbOptionsPage::instance();
328 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
329 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
332 return new CdbEngine(sp, masterEngine, op->options());
334 Q_UNUSED(masterEngine)
337 *errorMessage = QString::fromLatin1("Unsupported debug mode");
341 bool isCdbEngineEnabled()
344 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
350 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
352 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
355 static QString cdbBinary(const DebuggerStartParameters &sp)
357 if (!sp.debuggerCommand.isEmpty()) {
358 // Do not use a GDB binary if we got started for a project with MinGW runtime.
359 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
360 && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
361 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
362 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
364 return sp.debuggerCommand;
366 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
369 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
372 if (!isCdbEngineEnabled()) {
373 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
374 arg(sp.toolChainAbi.toString()));
375 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
376 check->settingsPage = CdbOptionsPage::settingsId();
380 if (!validMode(sp.startMode)) {
381 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
385 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
386 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
387 arg(sp.toolChainAbi.toString()));
391 if (cdbBinary(sp).isEmpty()) {
392 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
393 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
394 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
401 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
406 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
409 opts->push_back(new CdbOptionsPage);
415 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
417 static inline Utils::SavedAction *theAssemblerAction()
419 return debuggerCore()->action(OperateByInstruction);
422 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
423 DebuggerEngine *masterEngine, const OptionsPtr &options) :
424 DebuggerEngine(sp, masterEngine),
425 m_creatorExtPrefix("<qtcreatorcdbext>|"),
426 m_tokenPrefix("<token>"),
428 m_effectiveStartMode(NoStartMode),
431 m_specialStopMode(NoSpecialStop),
432 m_nextCommandToken(0),
433 m_currentBuiltinCommandIndex(-1),
434 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
435 m_operateByInstructionPending(true),
436 m_operateByInstruction(true), // Default CDB setting
437 m_notifyEngineShutdownOnTermination(false),
438 m_hasDebuggee(false),
440 m_sourceStepInto(false),
443 m_ignoreCdbOutput(false)
445 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
447 setObjectName(QLatin1String("CdbEngine"));
448 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
449 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
450 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
451 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
454 void CdbEngine::init()
456 m_effectiveStartMode = NoStartMode;
458 m_accessible = false;
459 m_specialStopMode = NoSpecialStop;
460 m_nextCommandToken = 0;
461 m_currentBuiltinCommandIndex = -1;
462 m_operateByInstructionPending = theAssemblerAction()->isChecked();
463 m_operateByInstruction = true; // Default CDB setting
464 m_notifyEngineShutdownOnTermination = false;
465 m_hasDebuggee = false;
466 m_sourceStepInto = false;
467 m_watchPointX = m_watchPointY = 0;
468 m_ignoreCdbOutput = false;
470 m_outputBuffer.clear();
471 m_builtinCommandQueue.clear();
472 m_extensionCommandQueue.clear();
473 m_extensionMessageBuffer.clear();
474 m_pendingBreakpointMap.clear();
475 m_customSpecialStopData.clear();
477 // Create local list of mappings in native separators
478 m_sourcePathMappings.clear();
479 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
480 if (!globalOptions->sourcePathMap.isEmpty()) {
481 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
482 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
483 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
484 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
485 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
486 QDir::toNativeSeparators(it.value())));
489 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
492 CdbEngine::~CdbEngine()
496 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
498 if (state() == InferiorStopOk) {
499 syncOperateByInstruction(operateByInstruction);
501 // To be set next time session becomes accessible
502 m_operateByInstructionPending = operateByInstruction;
506 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
509 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
510 if (m_operateByInstruction == operateByInstruction)
512 QTC_ASSERT(m_accessible, return; )
513 m_operateByInstruction = operateByInstruction;
514 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
515 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
518 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
519 TextEditor::ITextEditor *editor,
520 const DebuggerToolTipContext &contextIn)
523 qDebug() << Q_FUNC_INFO;
524 // Need a stopped debuggee and a cpp file in a valid frame
525 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
527 // Determine expression and function
530 DebuggerToolTipContext context = contextIn;
531 const QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
532 // Are we in the current stack frame
533 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
535 // No numerical or any other expressions [yet]
536 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
538 const QByteArray iname = QByteArray(localsPrefixC) + exp.toAscii();
539 const QModelIndex index = watchHandler()->itemIndex(iname);
540 if (!index.isValid())
542 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
543 tw->setContext(context);
544 tw->setDebuggerModel(LocalsWatch);
545 tw->setExpression(exp);
546 tw->acquireEngine(this);
547 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
551 // Determine full path to the CDB extension library.
552 QString CdbEngine::extensionLibraryName(bool is64Bit)
554 // Determine extension lib name and path to use
556 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
557 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
558 << '/' << QT_CREATOR_CDB_EXT << ".dll";
562 // Determine environment for CDB.exe, start out with run config and
563 // add CDB extension path merged with system value should there be one.
564 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
565 QString cdbExtensionPath)
567 // Determine CDB extension path from Qt Creator
568 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
569 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
570 if (!oldCdbExtensionPath.isEmpty()) {
571 cdbExtensionPath.append(QLatin1Char(';'));
572 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
574 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
575 // config, just to make sure, delete any existing entries
576 const QString cdbExtensionPathVariableAssign =
577 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
578 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
579 if (it->startsWith(cdbExtensionPathVariableAssign)) {
580 it = runConfigEnvironment.erase(it);
586 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
587 QDir::toNativeSeparators(cdbExtensionPath));
588 return runConfigEnvironment;
591 int CdbEngine::elapsedLogTime() const
593 const int elapsed = m_logTime.elapsed();
594 const int delta = elapsed - m_elapsedLogTime;
595 m_elapsedLogTime = elapsed;
599 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
600 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
603 qDebug("startConsole %s", qPrintable(sp.executable));
604 m_consoleStub.reset(new Utils::ConsoleProcess);
605 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
606 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
607 SLOT(consoleStubMessage(QString, bool)));
608 connect(m_consoleStub.data(), SIGNAL(processStarted()),
609 SLOT(consoleStubProcessStarted()));
610 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
611 SLOT(consoleStubExited()));
612 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
613 if (sp.environment.size())
614 m_consoleStub->setEnvironment(sp.environment);
615 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
616 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
622 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
625 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
627 if (state() == EngineSetupRequested) {
628 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
629 notifyEngineSetupFailed();
631 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
634 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
636 showMessage(msg, AppOutput);
640 void CdbEngine::consoleStubProcessStarted()
643 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
644 // Attach to console process.
645 DebuggerStartParameters attachParameters = startParameters();
646 attachParameters.executable.clear();
647 attachParameters.processArgs.clear();
648 attachParameters.attachPID = m_consoleStub->applicationPID();
649 attachParameters.startMode = AttachExternal;
650 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
651 QString errorMessage;
652 if (!launchCDB(attachParameters, &errorMessage)) {
653 showMessage(errorMessage, LogError);
654 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
655 notifyEngineSetupFailed();
659 void CdbEngine::consoleStubExited()
663 void CdbEngine::setupEngine()
666 qDebug(">setupEngine");
667 // Nag to add symbol server
668 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
669 &(m_options->symbolPaths)))
670 m_options->toSettings(Core::ICore::instance()->settings());
673 if (!m_logTime.elapsed())
675 QString errorMessage;
676 // Console: Launch the stub with the suspended application and attach to it
677 // CDB in theory has a command line option '-2' that launches a
678 // console, too, but that immediately closes when the debuggee quits.
679 // Use the Creator stub instead.
680 const DebuggerStartParameters &sp = startParameters();
681 const bool launchConsole = isConsole(sp);
682 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
683 const bool ok = launchConsole ?
684 startConsole(startParameters(), &errorMessage) :
685 launchCDB(startParameters(), &errorMessage);
687 qDebug("<setupEngine ok=%d", ok);
689 showMessage(errorMessage, LogError);
690 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
691 notifyEngineSetupFailed();
695 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
698 qDebug("launchCDB startMode=%d", sp.startMode);
699 const QChar blank(QLatin1Char(' '));
700 // Start engine which will run until initial breakpoint:
701 // Determine binary (force MSVC), extension lib name and path to use
702 // The extension is passed as relative name with the path variable set
703 //(does not work with absolute path names)
704 const QString executable = cdbBinary(sp);
705 if (executable.isEmpty()) {
706 *errorMessage = tr("There is no CDB executable specified.");
712 Utils::winIs64BitBinary(executable);
716 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
717 if (!extensionFi.isFile()) {
718 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
719 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
722 const QString extensionFileName = extensionFi.fileName();
724 QStringList arguments;
725 const bool isRemote = sp.startMode == AttachToRemote;
726 if (isRemote) { // Must be first
727 arguments << QLatin1String("-remote") << sp.remoteChannel;
729 arguments << (QLatin1String("-a") + extensionFileName);
731 // Source line info/No terminal breakpoint / Pull extension
732 arguments << QLatin1String("-lines") << QLatin1String("-G")
733 // register idle (debuggee stop) notification
734 << QLatin1String("-c")
735 << QString::fromAscii(".idle_cmd " + m_extensionCommandPrefixBA + "idle");
736 if (sp.useTerminal) // Separate console
737 arguments << QLatin1String("-2");
738 if (!m_options->symbolPaths.isEmpty())
739 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
740 if (!m_options->sourcePaths.isEmpty())
741 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
742 // Compile argument string preserving quotes
743 QString nativeArguments = m_options->additionalArguments;
744 switch (sp.startMode) {
747 if (!nativeArguments.isEmpty())
748 nativeArguments.push_back(blank);
749 nativeArguments += QDir::toNativeSeparators(sp.executable);
754 case AttachCrashedExternal:
755 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
756 if (sp.startMode == AttachCrashedExternal)
757 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
760 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
763 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
764 if (!nativeArguments.isEmpty())
765 nativeArguments.push_back(blank);
766 nativeArguments += sp.processArgs;
769 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
770 arg(QDir::toNativeSeparators(executable),
771 arguments.join(QString(blank)) + blank + nativeArguments,
772 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
773 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
774 showMessage(msg, LogMisc);
776 m_outputBuffer.clear();
777 const QStringList environment = sp.environment.size() == 0 ?
778 QProcessEnvironment::systemEnvironment().toStringList() :
779 sp.environment.toStringList();
780 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
781 if (!sp.workingDirectory.isEmpty())
782 m_process.setWorkingDirectory(sp.workingDirectory);
785 if (!nativeArguments.isEmpty()) // Appends
786 m_process.setNativeArguments(nativeArguments);
788 m_process.start(executable, arguments);
789 if (!m_process.waitForStarted()) {
790 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
791 arg(QDir::toNativeSeparators(executable), m_process.errorString());
795 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
797 const unsigned long pid = 0;
799 showMessage(QString::fromLatin1("%1 running as %2").
800 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
801 m_hasDebuggee = true;
802 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
804 const QByteArray loadCommand = QByteArray(".load ")
805 + extensionFileName.toLocal8Bit();
806 postCommand(loadCommand, 0);
807 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
808 notifyEngineSetupOk();
813 void CdbEngine::setupInferior()
816 qDebug("setupInferior");
817 attemptBreakpointSynchronization();
818 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
819 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
822 void CdbEngine::runEngine()
826 // Resume the threads frozen by the console stub.
827 if (isConsole(startParameters()))
828 postCommand("~* m", 0);
829 foreach (const QString &breakEvent, m_options->breakEvents)
830 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
834 bool CdbEngine::commandsPending() const
836 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
839 void CdbEngine::shutdownInferior()
842 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
843 isCdbProcessRunning());
845 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
847 qDebug("notifyInferiorShutdownOk");
848 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
849 notifyInferiorShutdownOk();
854 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
856 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
857 notifyInferiorShutdownOk();
859 // A command got stuck.
860 if (commandsPending()) {
861 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
862 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
863 notifyInferiorShutdownFailed();
866 if (!canInterruptInferior()) {
867 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
868 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
869 notifyInferiorShutdownFailed();
872 interruptInferior(); // Calls us again
876 /* shutdownEngine/processFinished:
877 * Note that in the case of launching a process by the debugger, the debugger
878 * automatically quits a short time after reporting the session becoming
879 * inaccessible without debuggee (notifyInferiorExited). In that case,
880 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
881 * as not to confuse the state engine.
884 void CdbEngine::shutdownEngine()
887 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
888 "accessible=%d,commands pending=%d",
889 stateName(state()), isCdbProcessRunning(), m_accessible,
892 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
893 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
894 notifyEngineShutdownOk();
898 // No longer trigger anything from messages
899 m_ignoreCdbOutput = true;
900 // Go for kill if there are commands pending.
901 if (m_accessible && !commandsPending()) {
902 // detach: Wait for debugger to finish.
903 if (m_effectiveStartMode == AttachExternal)
905 // Remote requires a bit more force to quit.
906 if (m_effectiveStartMode == AttachToRemote) {
907 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
908 postCommand("qq", 0);
912 m_notifyEngineShutdownOnTermination = true;
915 // Remote process. No can do, currently
916 m_notifyEngineShutdownOnTermination = true;
917 Utils::SynchronousProcess::stopProcess(m_process);
920 // Lost debuggee, debugger should quit anytime now
921 if (!m_hasDebuggee) {
922 m_notifyEngineShutdownOnTermination = true;
928 void CdbEngine::processFinished()
931 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
932 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
933 m_process.exitStatus(), m_process.exitCode());
935 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
937 showMessage(tr("CDB crashed"), LogError); // not in your life.
939 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
942 if (m_notifyEngineShutdownOnTermination) {
945 qDebug("notifyEngineIll");
946 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
949 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
950 notifyEngineShutdownOk();
953 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
954 // Otherwise, we take a shortcut.
955 if (isSlaveEngine()) {
956 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
957 notifyInferiorExited();
959 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
960 notifyEngineSpontaneousShutdown();
965 void CdbEngine::detachDebugger()
967 postCommand(".detach", 0);
970 static inline bool isWatchIName(const QByteArray &iname)
972 return iname.startsWith("watch");
975 void CdbEngine::updateWatchData(const WatchData &dataIn,
976 const WatchUpdateFlags & flags)
978 if (debug || debugLocals || debugWatches)
979 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
980 elapsedLogTime(), m_accessible, stateName(state()),
981 flags.tryIncremental,
982 qPrintable(dataIn.toString()));
984 if (!m_accessible) // Add watch data while running?
988 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
990 ByteArrayInputStream str(args);
991 str << dataIn.iname << " \"" << dataIn.exp << '"';
992 postExtensionCommand("addwatch", args, 0,
993 &CdbEngine::handleAddWatch, 0,
994 qVariantFromValue(dataIn));
998 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
999 WatchData data = dataIn;
1000 data.setAllUnneeded();
1001 watchHandler()->insertData(data);
1004 updateLocalVariable(dataIn.iname);
1007 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1009 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1011 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1012 if (reply->success) {
1013 updateLocalVariable(item.iname);
1015 item.setError(tr("Unable to add expression"));
1016 watchHandler()->insertData(item);
1017 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1018 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1019 reply->errorMessage), LogError);
1023 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1025 if (debuggerCore()->boolSetting(VerboseLog))
1026 str << blankSeparator << "-v";
1027 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1028 str << blankSeparator << "-c";
1029 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1030 if (!typeFormats.isEmpty())
1031 str << blankSeparator << "-T " << typeFormats;
1032 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1033 if (!individualFormats.isEmpty())
1034 str << blankSeparator << "-I " << individualFormats;
1037 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1039 const bool isWatch = isWatchIName(iname);
1041 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1042 QByteArray localsArguments;
1043 ByteArrayInputStream str(localsArguments);
1044 addLocalsOptions(str);
1046 const int stackFrame = stackHandler()->currentIndex();
1047 if (stackFrame < 0) {
1048 qWarning("Internal error; no stack frame in updateLocalVariable");
1051 str << blankSeparator << stackFrame;
1053 str << blankSeparator << iname;
1054 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1057 unsigned CdbEngine::debuggerCapabilities() const
1059 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1060 |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
1061 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1062 |BreakConditionCapability|TracePointCapability
1063 |BreakModuleCapability;
1066 void CdbEngine::executeStep()
1068 if (!m_operateByInstruction)
1069 m_sourceStepInto = true; // See explanation at handleStackTrace().
1070 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1071 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1072 notifyInferiorRunRequested();
1075 void CdbEngine::executeStepOut()
1077 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1078 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1079 notifyInferiorRunRequested();
1082 void CdbEngine::executeNext()
1084 postCommand(QByteArray("p"), 0); // Step over -> p
1085 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1086 notifyInferiorRunRequested();
1089 void CdbEngine::executeStepI()
1094 void CdbEngine::executeNextI()
1099 void CdbEngine::continueInferior()
1101 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1102 notifyInferiorRunRequested();
1103 doContinueInferior();
1106 void CdbEngine::doContinueInferior()
1108 postCommand(QByteArray("g"), 0);
1111 bool CdbEngine::canInterruptInferior() const
1113 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1116 void CdbEngine::interruptInferior()
1119 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1120 if (canInterruptInferior()) {
1121 doInterruptInferior(NoSpecialStop);
1123 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1124 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1125 notifyInferiorStopOk();
1126 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1127 notifyInferiorRunRequested();
1128 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1129 notifyInferiorRunOk();
1133 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1135 if (m_specialStopMode == NoSpecialStop)
1136 doInterruptInferior(CustomSpecialStop);
1137 m_customSpecialStopData.push_back(v);
1140 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1143 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1144 m_specialStopMode = sm;
1145 QString errorMessage;
1146 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1147 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1148 m_specialStopMode = oldSpecialMode;
1149 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1156 void CdbEngine::executeRunToLine(const ContextData &data)
1158 // Add one-shot breakpoint
1159 BreakpointParameters bp;
1161 bp.type =BreakpointByAddress;
1162 bp.address = data.address;
1164 bp.type =BreakpointByFileAndLine;
1165 bp.fileName = data.fileName;
1166 bp.lineNumber = data.lineNumber;
1168 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1172 void CdbEngine::executeRunToFunction(const QString &functionName)
1174 // Add one-shot breakpoint
1175 BreakpointParameters bp(BreakpointByFunction);
1176 bp.functionName = functionName;
1178 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1182 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1184 const Registers registers = registerHandler()->registers();
1185 QTC_ASSERT(regnr < registers.size(), return)
1186 // Value is decimal or 0x-hex-prefixed
1188 ByteArrayInputStream str(cmd);
1189 str << "r " << registers.at(regnr).name << '=' << value;
1190 postCommand(cmd, 0);
1194 void CdbEngine::executeJumpToLine(const ContextData &data)
1197 // Goto address directly.
1198 jumpToAddress(data.address);
1199 gotoLocation(Location(data.address));
1201 // Jump to source line: Resolve source line address and go to that location
1203 ByteArrayInputStream str(cmd);
1204 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1205 const QVariant cookie = qVariantFromValue(data);
1206 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1210 void CdbEngine::jumpToAddress(quint64 address)
1212 // Fake a jump to address by setting the PC register.
1213 QByteArray registerCmd;
1214 ByteArrayInputStream str(registerCmd);
1215 // PC-register depending on 64/32bit.
1216 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1217 str.setHexPrefix(true);
1218 str.setIntegerBase(16);
1220 postCommand(registerCmd, 0);
1223 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1225 if (cmd->reply.isEmpty())
1227 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1228 // Set register 'rip' to hex address and goto lcoation
1229 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1230 const int equalPos = answer.indexOf(" = ");
1233 answer.remove(0, equalPos + 3);
1234 answer.remove(QLatin1Char('`'));
1236 const quint64 address = answer.toLongLong(&ok, 16);
1237 if (ok && address) {
1238 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1239 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1240 jumpToAddress(address);
1241 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1245 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1248 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1250 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1251 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1256 ByteArrayInputStream str(cmd);
1257 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1258 postCommand(cmd, 0);
1259 // Update all locals in case we change a union or something pointed to
1260 // that affects other variables, too.
1264 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1266 int currentThreadId;
1267 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1268 threadsHandler()->setThreads(threads);
1269 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1270 forceCurrentThreadId : currentThreadId);
1273 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1276 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1277 if (reply->success) {
1279 data.fromString(reply->reply);
1281 // Continue sequence
1282 postCommandSequence(reply->commandSequence);
1284 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1288 void CdbEngine::executeDebuggerCommand(const QString &command)
1290 postCommand(command.toLocal8Bit(), QuietCommand);
1293 // Post command without callback
1294 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1297 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1298 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1299 if (!(flags & QuietCommand))
1300 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1301 m_process.write(cmd + '\n');
1304 // Post a built-in-command producing free-format output with a callback.
1305 // In order to catch the output, it is enclosed in 'echo' commands
1306 // printing a specially formatted token to be identifiable in the output.
1307 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1308 BuiltinCommandHandler handler,
1309 unsigned nextCommandFlag,
1310 const QVariant &cookie)
1312 if (!m_accessible) {
1313 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1314 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1315 showMessage(msg, LogError);
1318 if (!flags & QuietCommand)
1319 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1321 const int token = m_nextCommandToken++;
1322 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1324 m_builtinCommandQueue.push_back(pendingCommand);
1325 // Enclose command in echo-commands for token
1327 ByteArrayInputStream str(fullCmd);
1328 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1329 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1331 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1332 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1333 m_builtinCommandQueue.size(), nextCommandFlag);
1335 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1336 fullCmd.constData());
1337 m_process.write(fullCmd);
1340 // Post an extension command producing one-line output with a callback,
1341 // pass along token for identification in queue.
1342 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1343 const QByteArray &arguments,
1345 ExtensionCommandHandler handler,
1346 unsigned nextCommandFlag,
1347 const QVariant &cookie)
1349 if (!m_accessible) {
1350 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1351 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1352 showMessage(msg, LogError);
1356 const int token = m_nextCommandToken++;
1358 // Format full command with token to be recognizeable in the output
1360 ByteArrayInputStream str(fullCmd);
1361 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1362 if (!arguments.isEmpty())
1363 str << ' ' << arguments;
1365 if (!flags & QuietCommand)
1366 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1368 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1370 m_extensionCommandQueue.push_back(pendingCommand);
1371 // Enclose command in echo-commands for token
1373 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1374 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1375 m_extensionCommandQueue.size(), nextCommandFlag);
1376 m_process.write(fullCmd + '\n');
1379 void CdbEngine::activateFrame(int index)
1381 // TODO: assembler,etc
1384 const StackFrames &frames = stackHandler()->frames();
1385 QTC_ASSERT(index < frames.size(), return; )
1387 const StackFrame frame = frames.at(index);
1388 if (debug || debugLocals)
1389 qDebug("activateFrame idx=%d '%s' %d", index,
1390 qPrintable(frame.file), frame.line);
1391 stackHandler()->setCurrentIndex(index);
1392 const bool showAssembler = !frames.at(index).isUsable();
1393 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1394 watchHandler()->beginCycle();
1395 watchHandler()->endCycle();
1396 QAction *assemblerAction = theAssemblerAction();
1397 if (assemblerAction->isChecked()) {
1398 gotoLocation(frame);
1400 assemblerAction->trigger(); // Seems to trigger update
1403 gotoLocation(frame);
1408 void CdbEngine::updateLocals(bool forNewStackFrame)
1410 typedef QHash<QByteArray, int> WatcherHash;
1412 const int frameIndex = stackHandler()->currentIndex();
1413 if (frameIndex < 0) {
1414 watchHandler()->beginCycle();
1415 watchHandler()->endCycle();
1418 const StackFrame frame = stackHandler()->currentFrame();
1419 if (!frame.isUsable()) {
1420 watchHandler()->beginCycle();
1421 watchHandler()->endCycle();
1424 /* Watchers: Forcibly discard old symbol group as switching from
1425 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1426 * and cause errors as it seems to go 'stale' when switching threads.
1427 * Initial expand, get uninitialized and query */
1428 QByteArray arguments;
1429 ByteArrayInputStream str(arguments);
1432 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1433 if (!expanded.isEmpty()) {
1434 str << blankSeparator << "-e ";
1436 foreach(const QByteArray &e, expanded) {
1442 addLocalsOptions(str);
1443 // Uninitialized variables if desired
1444 if (debuggerCore()->boolSetting(UseCodeModel)) {
1445 QStringList uninitializedVariables;
1446 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1447 frame.function, frame.file, frame.line, &uninitializedVariables);
1448 if (!uninitializedVariables.isEmpty()) {
1449 str << blankSeparator << "-u ";
1451 foreach(const QString &u, uninitializedVariables) {
1454 str << localsPrefixC << u;
1458 // Perform watches synchronization
1459 str << blankSeparator << "-W";
1460 const WatcherHash watcherHash = WatchHandler::watcherNames();
1461 if (!watcherHash.isEmpty()) {
1462 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1463 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1464 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1468 // Required arguments: frame
1469 str << blankSeparator << frameIndex;
1470 watchHandler()->beginCycle();
1471 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1474 void CdbEngine::selectThread(int index)
1476 if (index < 0 || index == threadsHandler()->currentThread())
1480 const int newThreadId = threadsHandler()->threads().at(index).id;
1481 threadsHandler()->setCurrentThread(index);
1483 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1484 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1487 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1489 QTC_ASSERT(m_accessible, return;)
1491 ByteArrayInputStream str(cmd);
1492 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1493 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1494 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1497 // Parse: "00000000`77606060 cc int 3"
1498 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1500 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1501 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1502 DisassemblerLines disassemblerLines;
1503 foreach(const QByteArray &line, command->reply)
1504 disassemblerLines.appendLine(DisassemblerLine(QString::fromLatin1(line)));
1505 agent->setContents(disassemblerLines);
1508 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1510 if (!m_accessible) {
1511 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1515 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1518 ByteArrayInputStream str(args);
1519 str << addr << ' ' << length;
1520 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1521 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1524 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1526 QTC_ASSERT(!data.isEmpty(), return; )
1527 if (!m_accessible) {
1528 const MemoryChangeCookie cookie(addr, data);
1529 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1531 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1535 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1537 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1538 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1539 if (command->success) {
1540 const QByteArray data = QByteArray::fromBase64(command->reply);
1541 if (unsigned(data.size()) == memViewCookie.length)
1542 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1543 memViewCookie.address, data);
1545 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1549 void CdbEngine::reloadModules()
1551 postCommandSequence(CommandListModules);
1554 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1558 void CdbEngine::loadAllSymbols()
1562 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1564 Q_UNUSED(moduleName)
1567 void CdbEngine::reloadRegisters()
1569 postCommandSequence(CommandListRegisters);
1572 void CdbEngine::reloadSourceFiles()
1576 void CdbEngine::reloadFullStack()
1579 qDebug("%s", Q_FUNC_INFO);
1580 postCommandSequence(CommandListStack);
1583 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1585 if (reply->success) {
1586 m_inferiorPid = reply->reply.toUInt();
1587 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1588 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1589 notifyInferiorSetupOk();
1591 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1592 arg(QLatin1String(reply->errorMessage)), LogError);
1593 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1594 notifyInferiorSetupFailed();
1598 // Parse CDB gdbmi register syntax
1599 static inline Register parseRegister(const GdbMi &gdbmiReg)
1602 reg.name = gdbmiReg.findChild("name").data();
1603 const GdbMi description = gdbmiReg.findChild("description");
1604 if (description.type() != GdbMi::Invalid) {
1606 reg.name += description.data();
1609 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1613 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1615 if (reply->success) {
1617 value.fromString(reply->reply);
1618 if (value.type() == GdbMi::List) {
1620 modules.reserve(value.childCount());
1621 foreach (const GdbMi &gdbmiModule, value.children()) {
1623 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1624 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1625 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1626 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1627 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1628 module.symbolsRead = Module::ReadOk;
1629 modules.push_back(module);
1631 modulesHandler()->setModules(modules);
1633 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1634 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1637 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1638 arg(QLatin1String(reply->errorMessage)), LogError);
1640 postCommandSequence(reply->commandSequence);
1644 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1646 if (reply->success) {
1648 value.fromString(reply->reply);
1649 if (value.type() == GdbMi::List) {
1650 Registers registers;
1651 registers.reserve(value.childCount());
1652 foreach (const GdbMi &gdbmiReg, value.children())
1653 registers.push_back(parseRegister(gdbmiReg));
1654 registerHandler()->setRegisters(registers);
1656 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1657 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1660 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1661 arg(QLatin1String(reply->errorMessage)), LogError);
1663 postCommandSequence(reply->commandSequence);
1666 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1668 if (reply->success) {
1669 QList<WatchData> watchData;
1671 root.fromString(reply->reply);
1672 QTC_ASSERT(root.isList(), return ; )
1674 qDebug() << root.toString(true, 4);
1676 // Courtesy of GDB engine
1677 foreach (const GdbMi &child, root.children()) {
1679 dummy.iname = child.findChild("iname").data();
1680 dummy.name = QLatin1String(child.findChild("name").data());
1681 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1683 watchHandler()->insertBulkData(watchData);
1684 watchHandler()->endCycle();
1686 QDebug nsp = qDebug().nospace();
1687 nsp << "Obtained " << watchData.size() << " items:\n";
1688 foreach (const WatchData &wd, watchData)
1689 nsp << wd.toString() <<'\n';
1691 const bool forNewStackFrame = reply->cookie.toBool();
1692 if (forNewStackFrame)
1693 emit stackFrameCompleted();
1695 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1699 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1701 if (!reply->success)
1702 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1705 enum CdbExecutionStatus {
1706 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1707 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1708 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1709 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1710 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1711 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1712 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1714 static const char *cdbStatusName(unsigned long s)
1717 case CDB_STATUS_NO_CHANGE:
1721 case CDB_STATUS_GO_HANDLED:
1722 return "go_handled";
1723 case CDB_STATUS_GO_NOT_HANDLED:
1724 return "go_not_handled";
1725 case CDB_STATUS_STEP_OVER:
1727 case CDB_STATUS_STEP_INTO:
1729 case CDB_STATUS_BREAK:
1731 case CDB_STATUS_NO_DEBUGGEE:
1732 return "no_debuggee";
1733 case CDB_STATUS_STEP_BRANCH:
1734 return "step_branch";
1735 case CDB_STATUS_IGNORE_EVENT:
1736 return "ignore_event";
1737 case CDB_STATUS_RESTART_REQUESTED:
1738 return "restart_requested";
1739 case CDB_STATUS_REVERSE_GO:
1740 return "reverse_go";
1741 case CDB_STATUS_REVERSE_STEP_BRANCH:
1742 return "reverse_step_branch";
1743 case CDB_STATUS_REVERSE_STEP_OVER:
1744 return "reverse_step_over";
1745 case CDB_STATUS_REVERSE_STEP_INTO:
1746 return "reverse_step_into";
1751 /* Examine how to react to a stop. */
1752 enum StopActionFlags
1755 StopReportLog = 0x1,
1756 StopReportStatusMessage = 0x2,
1757 StopReportParseError = 0x4,
1758 StopShowExceptionMessageBox = 0x8,
1759 // Notify stop or just continue
1760 StopNotifyStop = 0x10,
1761 StopIgnoreContinue = 0x20,
1762 // Hit on break in artificial stop thread (created by DebugBreak()).
1763 StopInArtificialThread = 0x40,
1764 StopShutdownInProgress = 0x80 // Shutdown in progress
1767 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1768 const QString &threadId)
1770 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1771 .arg(id).arg(number).arg(threadId);
1774 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1775 const QByteArray &condition,
1776 const QString &threadId)
1778 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1779 .arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
1782 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1784 QString *exceptionBoxMessage,
1785 bool conditionalBreakPointTriggered)
1787 // Report stop reason (GDBMI)
1789 if (targetState() == DebuggerFinished)
1790 rc |= StopShutdownInProgress;
1792 qDebug("%s", stopReason.toString(true, 4).constData());
1793 const QByteArray reason = stopReason.findChild("reason").data();
1794 if (reason.isEmpty()) {
1795 *message = tr("Malformed stop response received.");
1796 rc |= StopReportParseError|StopNotifyStop;
1799 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1800 if (state() == InferiorStopOk) {
1801 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1802 arg(QString::fromAscii(reason));
1803 rc |= StopReportLog;
1806 const int threadId = stopReason.findChild("threadId").data().toInt();
1807 if (reason == "breakpoint") {
1808 // Note: Internal breakpoints (run to line) are reported with id=0.
1809 // Step out creates temporary breakpoints with id 10000.
1810 BreakpointId id = 0;
1812 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1813 if (breakpointIdG.isValid()) {
1814 id = breakpointIdG.data().toULongLong();
1815 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1816 const BreakpointResponse parameters = breakHandler()->response(id);
1817 // Trace point? Just report.
1818 number = parameters.number;
1819 if (parameters.tracepoint) {
1820 *message = msgTracePointTriggered(id, number, QString::number(threadId));
1821 return StopReportLog|StopIgnoreContinue;
1823 // Trigger evaluation of BP expression unless we are already in the response.
1824 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1825 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1826 QString::number(threadId));
1827 ConditionalBreakPointCookie cookie(id);
1828 cookie.stopReason = stopReason;
1829 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1830 return StopReportLog;
1836 if (id && breakHandler()->type(id) == Watchpoint) {
1837 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1839 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1841 rc |= StopReportStatusMessage|StopNotifyStop;
1844 if (reason == "exception") {
1845 WinException exception;
1846 exception.fromGdbMI(stopReason);
1847 QString description = exception.toString();
1849 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1850 // pulls DLLs. Avoid showing a 'stopped' Message box.
1851 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1852 || exception.exceptionCode == winExceptionWX86Breakpoint)
1853 return StopNotifyStop;
1854 if (exception.exceptionCode == winExceptionCtrlPressed) {
1855 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1856 *message = msgInterrupted();
1857 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1860 if (isDebuggerWinException(exception.exceptionCode)) {
1861 rc |= StopReportStatusMessage|StopNotifyStop;
1862 // Detect interruption by DebugBreak() and force a switch to thread 0.
1863 if (exception.function == "ntdll!DbgBreakPoint")
1864 rc |= StopInArtificialThread;
1865 *message = msgInterrupted();
1869 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1870 *message = description;
1871 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1874 *message = msgStopped(QLatin1String(reason));
1875 rc |= StopReportStatusMessage|StopNotifyStop;
1879 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1885 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1886 elapsedLogTime(), messageBA.constData(),
1887 stateName(state()), m_specialStopMode);
1889 // Switch source level debugging
1890 syncOperateByInstruction(m_operateByInstructionPending);
1892 // Engine-special stop reasons: Breakpoints and setup
1893 const SpecialStopMode specialStopMode = m_specialStopMode;
1895 m_specialStopMode = NoSpecialStop;
1897 switch(specialStopMode) {
1898 case SpecialStopSynchronizeBreakpoints:
1900 qDebug("attemptBreakpointSynchronization in special stop");
1901 attemptBreakpointSynchronization();
1902 doContinueInferior();
1904 case SpecialStopGetWidgetAt:
1905 postWidgetAtCommand();
1907 case CustomSpecialStop:
1908 foreach (const QVariant &data, m_customSpecialStopData)
1909 handleCustomSpecialStop(data);
1910 m_customSpecialStopData.clear();
1911 doContinueInferior();
1917 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1918 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1919 notifyEngineSetupOk();
1923 stopReason.fromString(messageBA);
1924 processStop(stopReason, false);
1927 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1929 // Further examine stop and report to user
1931 QString exceptionBoxMessage;
1932 int forcedThreadId = -1;
1933 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1934 conditionalBreakPointTriggered);
1935 // Do the non-blocking log reporting
1936 if (stopFlags & StopReportLog)
1937 showMessage(message, LogMisc);
1938 if (stopFlags & StopReportStatusMessage)
1939 showStatusMessage(message);
1940 if (stopFlags & StopReportParseError)
1941 showMessage(message, LogError);
1942 // Ignore things like WOW64, report tracepoints.
1943 if (stopFlags & StopIgnoreContinue) {
1944 postCommand("g", 0);
1947 // Notify about state and send off command sequence to get stack, etc.
1948 if (stopFlags & StopNotifyStop) {
1949 if (state() == InferiorStopRequested) {
1950 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1951 notifyInferiorStopOk();
1953 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1954 notifyInferiorSpontaneousStop();
1956 // Prevent further commands from being sent if shutdown is in progress
1957 if (stopFlags & StopShutdownInProgress) {
1958 showMessage(QString::fromLatin1("Shutdown request detected..."));
1961 const bool sourceStepInto = m_sourceStepInto;
1962 m_sourceStepInto = false;
1963 // Start sequence to get all relevant data.
1964 if (stopFlags & StopInArtificialThread) {
1965 showMessage(tr("Switching to main thread..."), LogMisc);
1966 postCommand("~0 s", 0);
1968 // Re-fetch stack again.
1969 postCommandSequence(CommandListStack);
1971 const GdbMi stack = stopReason.findChild("stack");
1972 if (stack.isValid()) {
1973 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1974 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1978 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1981 const GdbMi threads = stopReason.findChild("threads");
1982 if (threads.isValid()) {
1983 parseThreads(threads, forcedThreadId);
1985 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1987 // Fire off remaining commands asynchronously
1988 if (!m_pendingBreakpointMap.isEmpty())
1989 postCommandSequence(CommandListBreakPoints);
1990 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1991 postCommandSequence(CommandListRegisters);
1992 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
1993 postCommandSequence(CommandListModules);
1995 // After the sequence has been sent off and CDB is pondering the commands,
1996 // pop up a message box for exceptions.
1997 if (stopFlags & StopShowExceptionMessageBox)
1998 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2001 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2003 const DebuggerState s = state();
2004 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2008 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2009 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2012 case EngineShutdownRequested:
2015 case InferiorShutdownRequested:
2023 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2025 const DebuggerState s = state();
2028 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2032 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2033 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2036 case EngineSetupRequested:
2038 case EngineRunRequested:
2039 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2040 notifyEngineRunAndInferiorRunOk();
2043 case InferiorStopOk:
2044 // Inaccessible without debuggee (exit breakpoint)
2045 // We go for spontaneous engine shutdown instead.
2046 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2048 qDebug("Lost debuggeee");
2049 m_hasDebuggee = false;
2052 case InferiorRunRequested:
2053 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2054 notifyInferiorRunOk();
2057 case EngineShutdownRequested:
2064 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2067 QDebug nospace = qDebug().nospace();
2068 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2069 << ' ' << stateName(state());
2070 if (t == 'N' || debug > 1) {
2071 nospace << ' ' << message;
2073 nospace << ' ' << message.size() << " bytes";
2077 // Is there a reply expected, some command queued?
2078 if (t == 'R' || t == 'N') {
2079 if (token == -1) { // Default token, user typed in extension command
2080 showMessage(QString::fromLatin1(message), LogMisc);
2083 const int index = indexOfCommand(m_extensionCommandQueue, token);
2085 // Did the command finish? Take off queue and complete, invoke CB
2086 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2088 command->success = true;
2089 command->reply = message;
2091 command->success = false;
2092 command->errorMessage = message;
2095 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2096 command->command.constData(), command->token, m_extensionCommandQueue.size());
2097 if (command->handler)
2098 (this->*(command->handler))(command);
2103 if (what == "debuggee_output") {
2104 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2108 if (what == "event") {
2109 showStatusMessage(QString::fromAscii(message), 5000);
2113 if (what == "session_accessible") {
2114 if (!m_accessible) {
2115 m_accessible = true;
2116 handleSessionAccessible(message.toULong());
2121 if (what == "session_inaccessible") {
2123 m_accessible = false;
2124 handleSessionInaccessible(message.toULong());
2129 if (what == "session_idle") {
2130 handleSessionIdle(message);
2134 if (what == "exception") {
2135 WinException exception;
2137 gdbmi.fromString(message);
2138 exception.fromGdbMI(gdbmi);
2139 const QString message = exception.toString(true);
2140 showStatusMessage(message);
2141 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2142 if (exception.exceptionCode == winExceptionCppException)
2143 showMessage(message + QLatin1Char('\n'), AppOutput);
2151 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2152 enum { CdbPromptLength = 7 };
2154 static inline bool isCdbPrompt(const QByteArray &c)
2156 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2157 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2158 && std::isdigit(c.at(4));
2161 // Check for '<token>32>' or '<token>32<'
2162 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2163 int *token, bool *isStart)
2167 const int tokenPrefixSize = tokenPrefix.size();
2168 const int size = c.size();
2169 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2171 switch (c.at(size - 1)) {
2181 if (!c.startsWith(tokenPrefix))
2184 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2188 void CdbEngine::parseOutputLine(QByteArray line)
2190 // The hooked output callback in the extension suppresses prompts,
2191 // it should happen only in initial and exit stages. Note however that
2192 // if the output is not hooked, sequences of prompts are possible which
2193 // can mix things up.
2194 while (isCdbPrompt(line))
2195 line.remove(0, CdbPromptLength);
2196 // An extension notification (potentially consisting of several chunks)
2197 if (line.startsWith(m_creatorExtPrefix)) {
2198 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2199 const char type = line.at(m_creatorExtPrefix.size());
2201 const int tokenPos = m_creatorExtPrefix.size() + 2;
2202 const int tokenEndPos = line.indexOf('|', tokenPos);
2203 QTC_ASSERT(tokenEndPos != -1, return)
2204 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2206 const int remainingChunksPos = tokenEndPos + 1;
2207 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2208 QTC_ASSERT(remainingChunksEndPos != -1, return)
2209 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2210 // const char 'serviceName'
2211 const int whatPos = remainingChunksEndPos + 1;
2212 const int whatEndPos = line.indexOf('|', whatPos);
2213 QTC_ASSERT(whatEndPos != -1, return)
2214 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2215 // Build up buffer, call handler once last chunk was encountered
2216 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2217 if (remainingChunks == 0) {
2218 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2219 m_extensionMessageBuffer.clear();
2223 // Check for command start/end tokens within which the builtin command
2224 // output is enclosed
2226 bool isStartToken = false;
2227 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2229 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2230 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2232 // If there is a current command, wait for end of output indicated by token,
2233 // command, trigger handler and finish, else append to its output.
2234 if (m_currentBuiltinCommandIndex != -1) {
2235 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2236 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2237 if (isCommandToken) {
2238 // Did the command finish? Invoke callback and remove from queue.
2240 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2241 currentCommand->command.constData(), currentCommand->token,
2242 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2243 QTC_ASSERT(token == currentCommand->token, return; );
2244 if (currentCommand->handler)
2245 (this->*(currentCommand->handler))(currentCommand);
2246 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2247 m_currentBuiltinCommandIndex = -1;
2249 // Record output of current command
2250 currentCommand->reply.push_back(line);
2253 } // m_currentCommandIndex
2254 if (isCommandToken) {
2255 // Beginning command token encountered, start to record output.
2256 const int index = indexOfCommand(m_builtinCommandQueue, token);
2257 QTC_ASSERT(isStartToken && index != -1, return; );
2258 m_currentBuiltinCommandIndex = index;
2259 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2261 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2265 showMessage(QString::fromLocal8Bit(line), LogMisc);
2268 void CdbEngine::readyReadStandardOut()
2270 if (m_ignoreCdbOutput)
2272 m_outputBuffer += m_process.readAllStandardOutput();
2273 // Split into lines and parse line by line.
2275 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2276 if (endOfLinePos == -1) {
2280 QByteArray line = m_outputBuffer.left(endOfLinePos);
2281 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2282 line.truncate(line.size() - 1);
2283 parseOutputLine(line);
2284 m_outputBuffer.remove(0, endOfLinePos + 1);
2289 void CdbEngine::readyReadStandardError()
2291 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2294 void CdbEngine::processError()
2296 showMessage(m_process.errorString(), LogError);
2300 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2301 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2303 QByteArray cmd(cmdC);
2304 ByteArrayInputStream str(cmd);
2305 foreach(const BreakpointData *bp, bps)
2306 str << ' ' << bp->bpNumber;
2311 bool CdbEngine::stateAcceptsBreakpointChanges() const
2315 case InferiorStopOk:
2323 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2325 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2326 if (!DebuggerEngine::isCppBreakpoint(data))
2328 switch (data.type) {
2330 case BreakpointAtFork:
2331 case BreakpointAtVFork:
2332 case BreakpointAtSysCall:
2335 case BreakpointByFileAndLine:
2336 case BreakpointByFunction:
2337 case BreakpointByAddress:
2338 case BreakpointAtThrow:
2339 case BreakpointAtCatch:
2340 case BreakpointAtMain:
2341 case BreakpointAtExec:
2347 void CdbEngine::attemptBreakpointSynchronization()
2350 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2351 // Check if there is anything to be done at all.
2352 BreakHandler *handler = breakHandler();
2353 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2354 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2355 if (acceptsBreakpoint(id))
2356 handler->setEngine(id, this);
2358 // Quick check: is there a need to change something? - Populate module cache
2359 bool changed = false;
2360 const BreakpointIds ids = handler->engineBreakpointIds(this);
2361 foreach (BreakpointId id, ids) {
2362 switch (handler->state(id)) {
2363 case BreakpointInsertRequested:
2364 case BreakpointRemoveRequested:
2365 case BreakpointChangeRequested:
2368 case BreakpointInserted: {
2369 // Collect the new modules matching the files.
2370 // In the future, that information should be obtained from the build system.
2371 const BreakpointParameters &data = handler->breakpointData(id);
2372 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2373 m_fileNameModuleHash.insert(data.fileName, data.module);
2381 if (debugBreakpoints)
2382 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2383 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2387 if (!m_accessible) {
2389 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2390 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2393 // Add/Change breakpoints and store pending ones in map, since
2394 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2395 // handleBreakPoints will the complete that information and set it on the break handler.
2396 bool addedChanged = false;
2397 foreach (BreakpointId id, ids) {
2398 BreakpointParameters parameters = handler->breakpointData(id);
2399 BreakpointResponse response;
2400 response.fromParameters(parameters);
2401 // If we encountered that file and have a module for it: Add it.
2402 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2403 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2404 if (it != m_fileNameModuleHash.constEnd())
2405 parameters.module = it.value();
2407 switch (handler->state(id)) {
2408 case BreakpointInsertRequested:
2409 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2410 if (!parameters.enabled)
2411 postCommand("bd " + QByteArray::number(id), 0);
2412 handler->notifyBreakpointInsertProceeding(id);
2413 handler->notifyBreakpointInsertOk(id);
2414 m_pendingBreakpointMap.insert(id, response);
2415 addedChanged = true;
2416 // Ensure enabled/disabled is correct in handler and line number is there.
2417 handler->setResponse(id, response);
2418 if (debugBreakpoints)
2419 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2421 case BreakpointChangeRequested:
2422 handler->notifyBreakpointChangeProceeding(id);
2423 if (debugBreakpoints)
2424 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2425 qPrintable(parameters.toString()));
2426 if (parameters.enabled != handler->response(id).enabled) {
2427 // Change enabled/disabled breakpoints without triggering update.
2428 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2429 response.pending = false;
2430 response.enabled = parameters.enabled;
2431 handler->setResponse(id, response);
2433 // Delete and re-add, triggering update
2434 addedChanged = true;
2435 postCommand("bc " + QByteArray::number(id), 0);
2436 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2437 m_pendingBreakpointMap.insert(id, response);
2439 handler->notifyBreakpointChangeOk(id);
2441 case BreakpointRemoveRequested:
2442 postCommand("bc " + QByteArray::number(id), 0);
2443 handler->notifyBreakpointRemoveProceeding(id);
2444 handler->notifyBreakpointRemoveOk(id);
2445 m_pendingBreakpointMap.remove(id);
2451 // List breakpoints and send responses
2453 postCommandSequence(CommandListBreakPoints);
2456 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2457 // manager to correctly process it) and convert to clean path.
2458 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2461 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2462 if (it != m_normalizedFileCache.constEnd())
2464 if (debugSourceMapping)
2465 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2466 // Do we have source path mappings? ->Apply.
2467 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2469 // Up/lower case normalization according to Windows.
2471 QString normalized = winNormalizeFileName(fileName);
2473 QString normalized = fileName;
2475 if (debugSourceMapping)
2476 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2477 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2478 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2479 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2481 // At least upper case drive letter if failed.
2482 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2483 result.fileName[0] = result.fileName.at(0).toUpper();
2485 m_normalizedFileCache.insert(f, result);
2486 if (debugSourceMapping)
2487 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2491 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2492 // has more processing.
2493 static StackFrames parseFrames(const GdbMi &gdbmi)
2496 const int count = gdbmi.childCount();
2498 for (int i = 0; i < count; i++) {
2499 const GdbMi &frameMi = gdbmi.childAt(i);
2502 const GdbMi fullName = frameMi.findChild("fullname");
2503 if (fullName.isValid()) {
2504 frame.file = QFile::decodeName(fullName.data());
2505 frame.line = frameMi.findChild("line").data().toInt();
2506 frame.usable = false; // To be decided after source path mapping.
2508 frame.function = QLatin1String(frameMi.findChild("func").data());
2509 frame.from = QLatin1String(frameMi.findChild("from").data());
2510 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2511 rc.push_back(frame);
2516 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2518 // Parse frames, find current. Special handling for step into:
2519 // When stepping into on an actual function (source mode) by executing 't', an assembler
2520 // frame pointing at the jmp instruction is hit (noticeable by top function being
2521 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2522 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2524 StackFrames frames = parseFrames(data);
2525 const int count = frames.size();
2526 for (int i = 0; i < count; i++) {
2527 const bool hasFile = !frames.at(i).file.isEmpty();
2528 // jmp-frame hit by step into, do another 't' and abort sequence.
2529 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2530 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2531 return ParseStackStepInto;
2534 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2535 frames[i].file = fileName.fileName;
2536 frames[i].usable = fileName.exists;
2537 if (current == -1 && frames[i].usable)
2541 if (count && current == -1) // No usable frame, use assembly.
2544 stackHandler()->setFrames(frames);
2545 activateFrame(current);
2549 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2551 if (command->success) {
2553 data.fromString(command->reply);
2554 parseStackTrace(data, false);
2555 postCommandSequence(command->commandSequence);
2557 showMessage(command->errorMessage, LogError);
2561 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2564 if (command->success) {
2565 value = command->reply.toInt();
2567 showMessage(command->errorMessage, LogError);
2569 // Is this a conditional breakpoint?
2570 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2571 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2572 const QString message = value ?
2573 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2574 arg(value).arg(cookie.id) :
2575 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2577 showMessage(message, LogMisc);
2578 // Stop if evaluation is true, else continue
2580 processStop(cookie.stopReason, true);
2582 postCommand("g", 0);
2587 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2589 if (exp.contains(' ') && !exp.startsWith('"')) {
2593 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2596 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2598 postCommandSequence(command->commandSequence);
2601 // Post a sequence of standard commands: Trigger next once one completes successfully
2602 void CdbEngine::postCommandSequence(unsigned mask)
2605 qDebug("postCommandSequence 0x%x\n", mask);
2609 if (mask & CommandListThreads) {
2610 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2613 if (mask & CommandListStack) {
2614 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2617 if (mask & CommandListRegisters) {
2618 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2619 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2622 if (mask & CommandListModules) {
2623 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2626 if (mask & CommandListBreakPoints) {
2627 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2628 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2633 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2635 bool success = false;
2638 if (!reply->success) {
2639 message = QString::fromAscii(reply->errorMessage);
2642 // Should be "namespace::QWidget:0x555"
2643 QString watchExp = QString::fromAscii(reply->reply);
2644 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2646 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2649 // 0x000 -> nothing found
2650 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2651 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2654 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2655 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2656 watchExp.insert(0, QLatin1String("*("));
2657 watchHandler()->watchExpression(watchExp);
2661 showMessage(message, LogWarning);
2662 m_watchPointX = m_watchPointY = 0;
2665 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2668 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2672 str.setIntegerBase(16);
2673 str << ", at 0x" << r.address;
2674 str.setIntegerBase(10);
2677 str << ", disabled";
2678 if (!r.module.isEmpty())
2679 str << ", module: '" << r.module << '\'';
2683 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2685 if (debugBreakpoints)
2686 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2687 if (!reply->success) {
2688 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2692 value.fromString(reply->reply);
2693 if (value.type() != GdbMi::List) {
2694 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2697 handleBreakPoints(value);
2700 void CdbEngine::handleBreakPoints(const GdbMi &value)
2702 // Report all obtained parameters back. Note that not all parameters are reported
2703 // back, so, match by id and complete
2704 if (debugBreakpoints)
2705 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2707 QTextStream str(&message);
2708 BreakHandler *handler = breakHandler();
2709 foreach (const GdbMi &breakPointG, value.children()) {
2710 BreakpointResponse reportedResponse;
2711 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2712 if (debugBreakpoints)
2713 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2714 qPrintable(reportedResponse.toString()));
2716 if (!reportedResponse.pending) {
2717 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2718 if (it != m_pendingBreakpointMap.end()) {
2719 // Complete the response and set on handler.
2720 BreakpointResponse ¤tResponse = it.value();
2721 currentResponse.number = reportedResponse.number;
2722 currentResponse.address = reportedResponse.address;
2723 currentResponse.module = reportedResponse.module;
2724 currentResponse.pending = reportedResponse.pending;
2725 currentResponse.enabled = reportedResponse.enabled;
2726 formatCdbBreakPointResponse(id, currentResponse, str);
2727 if (debugBreakpoints)
2728 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2729 handler->setResponse(id, currentResponse);
2730 m_pendingBreakpointMap.erase(it);
2732 } // not pending reported
2734 if (m_pendingBreakpointMap.empty()) {
2735 str << QLatin1String("All breakpoints have been resolved.\n");
2737 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2739 showMessage(message, LogMisc);
2742 void CdbEngine::watchPoint(const QPoint &p)
2744 m_watchPointX = p.x();
2745 m_watchPointY = p.y();
2747 case InferiorStopOk:
2748 postWidgetAtCommand();
2751 // "Select Widget to Watch" from a running application is currently not
2752 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2753 // but requires some work as not to confuse the engine by state-change notifications
2754 // emitted by the debuggee function call.
2755 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2758 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2759 arg(QString::fromAscii(stateName(state()))), LogWarning);
2764 void CdbEngine::postWidgetAtCommand()
2766 QByteArray arguments = QByteArray::number(m_watchPointX);
2767 arguments.append(' ');
2768 arguments.append(QByteArray::number(m_watchPointY));
2769 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2772 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2774 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2775 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2776 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2781 } // namespace Internal
2782 } // namespace Debugger