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 info@qt.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 <TranslationUnit.h>
61 #include <coreplugin/icore.h>
62 #include <texteditor/itexteditor.h>
63 #include <projectexplorer/abi.h>
64 #include <projectexplorer/projectexplorerconstants.h>
66 #include <utils/synchronousprocess.h>
67 #include <utils/winutils.h>
68 #include <utils/qtcassert.h>
69 #include <utils/savedaction.h>
70 #include <utils/consoleprocess.h>
71 #include <utils/fileutils.h>
73 #include <cplusplus/findcdbbreakpoint.h>
74 #include <cplusplus/CppDocument.h>
75 #include <cplusplus/ModelManagerInterface.h>
77 #include <QtCore/QCoreApplication>
78 #include <QtCore/QFileInfo>
79 #include <QtCore/QDir>
80 #include <QtCore/QDebug>
81 #include <QtCore/QTextStream>
82 #include <QtCore/QDateTime>
83 #include <QtGui/QToolTip>
84 #include <QtGui/QMainWindow>
85 #include <QtGui/QMessageBox>
88 # include <utils/winutils.h>
89 # include "dbgwinutils.h"
94 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
95 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
98 enum { debugLocals = 0 };
99 enum { debugSourceMapping = 0 };
100 enum { debugWatches = 0 };
101 enum { debugBreakpoints = 0 };
104 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
106 # define STATE_DEBUG(state, func, line, notifyFunc)
110 \class Debugger::Internal::CdbEngine
112 Cdb engine version 2: Run the CDB process on pipes and parse its output.
113 The engine relies on a CDB extension Qt Creator provides as an extension
114 library (32/64bit), which is loaded into cdb.exe. It serves to:
117 \o Notify the engine about the state of the debugging session:
119 \o idle: (hooked up with .idle_cmd) debuggee stopped
120 \o accessible: Debuggee stopped, cdb.exe accepts commands
121 \o inaccessible: Debuggee runs, no way to post commands
122 \o session active/inactive: Lost debuggee, terminating.
124 \o Hook up with output/event callbacks and produce formatted output to be able
125 to catch application output and exceptions.
126 \o Provide some extension commands that produce output in a standardized (GDBMI)
127 format that ends up in handleExtensionMessage(), for example:
129 \o pid Return debuggee pid for interrupting.
130 \o locals Print locals from SymbolGroup
131 \o expandLocals Expand locals in symbol group
132 \o registers, modules, threads
136 Debugger commands can be posted by calling:
140 \o postCommand(): Does not expect a reply
141 \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
142 that is captured by enclosing it in special tokens using the 'echo' command and
143 then invokes a callback with a CdbBuiltinCommand structure.
144 \o postExtensionCommand(): Run a command provided by the extension producing
145 one-line output and invoke a callback with a CdbExtensionCommand structure
146 (output is potentially split up in chunks).
151 [Console: The console stub launches the process. On process startup,
152 launchCDB() is called with AttachExternal].
153 setupEngine() calls launchCDB() with the startparameters. The debuggee
154 runs into the initial breakpoint (session idle). EngineSetupOk is
155 notified (inferior still stopped). setupInferior() is then called
156 which does breakpoint synchronization and issues the extension 'pid'
157 command to obtain the inferior pid (which also hooks up the output callbacks).
158 handlePid() notifies notifyInferiorSetupOk.
159 runEngine() is then called which issues 'g' to continue the inferior.
160 Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
161 when the inferior exits (except attach modes).
164 using namespace ProjectExplorer;
169 static const char localsPrefixC[] = "local.";
171 struct MemoryViewCookie
173 explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
174 quint64 addr = 0, quint64 l = 0) :
175 agent(a), editorToken(e), address(addr), length(l)
179 QObject *editorToken;
184 struct MemoryChangeCookie
186 explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
187 address(addr), data(d) {}
193 struct ConditionalBreakPointCookie
195 ConditionalBreakPointCookie(BreakpointId i = BreakpointId()) : id(i) {}
200 } // namespace Internal
201 } // namespace Debugger
203 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
204 Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
205 Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
210 static inline bool isCreatorConsole(const DebuggerStartParameters &sp, const CdbOptions &o)
212 return !o.cdbConsole && sp.useTerminal
213 && (sp.startMode == StartInternal || sp.startMode == StartExternal);
217 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
219 QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
220 debuggerCore()->mainWindow());
221 mb->setAttribute(Qt::WA_DeleteOnClose);
226 // Base data structure for command queue entries with callback
227 struct CdbCommandBase
229 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
232 CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
233 unsigned nc, const QVariant &cookie);
239 // Continue with another commands as specified in CommandSequenceFlags
240 unsigned commandSequence;
243 CdbCommandBase::CdbCommandBase() :
244 token(0), flags(0), commandSequence(0)
248 CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
249 unsigned nc, const QVariant &c) :
250 token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
254 // Queue entry for builtin commands producing free-format
255 // line-by-line output.
256 struct CdbBuiltinCommand : public CdbCommandBase
258 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
260 CdbBuiltinCommand() {}
261 CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
263 unsigned nc, const QVariant &cookie) :
264 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
268 QByteArray joinedReply() const;
270 CommandHandler handler;
271 QList<QByteArray> reply;
274 QByteArray CdbBuiltinCommand::joinedReply() const
279 answer.reserve(120 * reply.size());
280 foreach (const QByteArray &l, reply) {
287 // Queue entry for Qt Creator extension commands producing one-line
288 // output with success flag and error message.
289 struct CdbExtensionCommand : public CdbCommandBase
291 typedef CdbEngine::ExtensionCommandHandler CommandHandler;
293 CdbExtensionCommand() : success(false) {}
294 CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
296 unsigned nc, const QVariant &cookie) :
297 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
299 CommandHandler handler;
301 QByteArray errorMessage;
305 template <class CommandPtrType>
306 int indexOfCommand(const QList<CommandPtrType> &l, int token)
308 const int count = l.size();
309 for (int i = 0; i < count; i++)
310 if (l.at(i)->token == token)
315 static inline bool validMode(DebuggerStartMode sm)
328 // Accessed by RunControlFactory
329 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
330 DebuggerEngine *masterEngine, QString *errorMessage)
333 CdbOptionsPage *op = CdbOptionsPage::instance();
334 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
335 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
338 return new CdbEngine(sp, masterEngine, op->options());
340 Q_UNUSED(masterEngine)
343 *errorMessage = QString::fromLatin1("Unsupported debug mode");
347 bool isCdbEngineEnabled()
350 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
356 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
358 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
361 static QString cdbBinary(const DebuggerStartParameters &sp)
363 if (!sp.debuggerCommand.isEmpty()) {
364 // Do not use a GDB binary if we got started for a project with MinGW runtime.
365 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
366 && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
367 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
368 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
370 return sp.debuggerCommand;
372 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
375 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
378 if (!isCdbEngineEnabled()) {
379 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
380 arg(sp.toolChainAbi.toString()));
381 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
382 check->settingsPage = CdbOptionsPage::settingsId();
386 if (!validMode(sp.startMode)) {
387 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
391 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
392 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
393 arg(sp.toolChainAbi.toString()));
397 if (cdbBinary(sp).isEmpty()) {
398 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
399 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
400 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
407 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
412 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
415 opts->push_back(new CdbOptionsPage);
421 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
423 static inline Utils::SavedAction *theAssemblerAction()
425 return debuggerCore()->action(OperateByInstruction);
428 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
429 DebuggerEngine *masterEngine, const OptionsPtr &options) :
430 DebuggerEngine(sp, masterEngine),
431 m_creatorExtPrefix("<qtcreatorcdbext>|"),
432 m_tokenPrefix("<token>"),
434 m_effectiveStartMode(NoStartMode),
436 m_specialStopMode(NoSpecialStop),
437 m_nextCommandToken(0),
438 m_currentBuiltinCommandIndex(-1),
439 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
440 m_operateByInstructionPending(true),
441 m_operateByInstruction(true), // Default CDB setting
442 m_notifyEngineShutdownOnTermination(false),
443 m_hasDebuggee(false),
445 m_sourceStepInto(false),
448 m_ignoreCdbOutput(false)
450 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
452 setObjectName(QLatin1String("CdbEngine"));
453 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
454 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
455 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
456 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
459 void CdbEngine::init()
461 m_effectiveStartMode = NoStartMode;
462 notifyInferiorPid(0);
463 m_accessible = false;
464 m_specialStopMode = NoSpecialStop;
465 m_nextCommandToken = 0;
466 m_currentBuiltinCommandIndex = -1;
467 m_operateByInstructionPending = theAssemblerAction()->isChecked();
468 m_operateByInstruction = true; // Default CDB setting
469 m_notifyEngineShutdownOnTermination = false;
470 m_hasDebuggee = false;
471 m_sourceStepInto = false;
472 m_watchPointX = m_watchPointY = 0;
473 m_ignoreCdbOutput = false;
475 m_outputBuffer.clear();
476 m_builtinCommandQueue.clear();
477 m_extensionCommandQueue.clear();
478 m_extensionMessageBuffer.clear();
479 m_pendingBreakpointMap.clear();
480 m_customSpecialStopData.clear();
482 // Create local list of mappings in native separators
483 m_sourcePathMappings.clear();
484 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
485 if (!globalOptions->sourcePathMap.isEmpty()) {
486 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
487 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
488 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
489 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
490 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
491 QDir::toNativeSeparators(it.value())));
494 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
497 CdbEngine::~CdbEngine()
501 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
503 // To be set next time session becomes accessible
504 m_operateByInstructionPending = operateByInstruction;
505 if (state() == InferiorStopOk)
506 syncOperateByInstruction(operateByInstruction);
509 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
512 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
513 if (m_operateByInstruction == operateByInstruction)
515 QTC_ASSERT(m_accessible, return; )
516 m_operateByInstruction = operateByInstruction;
517 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
518 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
521 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
522 TextEditor::ITextEditor *editor,
523 const DebuggerToolTipContext &contextIn)
526 qDebug() << Q_FUNC_INFO;
527 // Need a stopped debuggee and a cpp file in a valid frame
528 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
530 // Determine expression and function
533 DebuggerToolTipContext context = contextIn;
534 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
535 // Are we in the current stack frame
536 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
538 // No numerical or any other expressions [yet]
539 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
541 // Can this be found as a local variable?
542 const QByteArray localsPrefix(localsPrefixC);
543 QByteArray iname = localsPrefix + exp.toAscii();
544 QModelIndex index = watchHandler()->itemIndex(iname);
545 if (!index.isValid()) {
546 // Nope, try a 'local.this.m_foo'.
547 exp.prepend(QLatin1String("this."));
548 iname.insert(localsPrefix.size(), "this.");
549 index = watchHandler()->itemIndex(iname);
550 if (!index.isValid())
553 DebuggerToolTipWidget *tw = new DebuggerToolTipWidget;
554 tw->setContext(context);
555 tw->setDebuggerModel(LocalsWatch);
556 tw->setExpression(exp);
557 tw->acquireEngine(this);
558 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
562 // Determine full path to the CDB extension library.
563 QString CdbEngine::extensionLibraryName(bool is64Bit)
565 // Determine extension lib name and path to use
567 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
568 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
569 << '/' << QT_CREATOR_CDB_EXT << ".dll";
573 // Determine environment for CDB.exe, start out with run config and
574 // add CDB extension path merged with system value should there be one.
575 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
576 QString cdbExtensionPath)
578 // Determine CDB extension path from Qt Creator
579 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
580 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
581 if (!oldCdbExtensionPath.isEmpty()) {
582 cdbExtensionPath.append(QLatin1Char(';'));
583 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
585 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
586 // config, just to make sure, delete any existing entries
587 const QString cdbExtensionPathVariableAssign =
588 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
589 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
590 if (it->startsWith(cdbExtensionPathVariableAssign)) {
591 it = runConfigEnvironment.erase(it);
597 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
598 QDir::toNativeSeparators(cdbExtensionPath));
599 return runConfigEnvironment;
602 int CdbEngine::elapsedLogTime() const
604 const int elapsed = m_logTime.elapsed();
605 const int delta = elapsed - m_elapsedLogTime;
606 m_elapsedLogTime = elapsed;
610 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
611 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
614 qDebug("startConsole %s", qPrintable(sp.executable));
615 m_consoleStub.reset(new Utils::ConsoleProcess);
616 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
617 connect(m_consoleStub.data(), SIGNAL(processError(QString)),
618 SLOT(consoleStubError(QString)));
619 connect(m_consoleStub.data(), SIGNAL(processStarted()),
620 SLOT(consoleStubProcessStarted()));
621 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
622 SLOT(consoleStubExited()));
623 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
624 if (sp.environment.size())
625 m_consoleStub->setEnvironment(sp.environment);
626 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
627 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
633 void CdbEngine::consoleStubError(const QString &msg)
636 qDebug("consoleStubProcessMessage() in %s %s", stateName(state()), qPrintable(msg));
637 if (state() == EngineSetupRequested) {
638 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
639 notifyEngineSetupFailed();
641 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
644 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
647 void CdbEngine::consoleStubProcessStarted()
650 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
651 // Attach to console process.
652 DebuggerStartParameters attachParameters = startParameters();
653 attachParameters.executable.clear();
654 attachParameters.processArgs.clear();
655 attachParameters.attachPID = m_consoleStub->applicationPID();
656 attachParameters.startMode = AttachExternal;
657 attachParameters.useTerminal = false;
658 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
659 QString errorMessage;
660 if (!launchCDB(attachParameters, &errorMessage)) {
661 showMessage(errorMessage, LogError);
662 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
663 notifyEngineSetupFailed();
667 void CdbEngine::consoleStubExited()
671 void CdbEngine::setupEngine()
674 qDebug(">setupEngine");
675 // Nag to add symbol server
676 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
677 &(m_options->symbolPaths)))
678 m_options->toSettings(Core::ICore::instance()->settings());
681 if (!m_logTime.elapsed())
683 QString errorMessage;
684 // Console: Launch the stub with the suspended application and attach to it
685 // CDB in theory has a command line option '-2' that launches a
686 // console, too, but that immediately closes when the debuggee quits.
687 // Use the Creator stub instead.
688 const DebuggerStartParameters &sp = startParameters();
689 const bool launchConsole = isCreatorConsole(sp, *m_options);
690 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
691 const bool ok = launchConsole ?
692 startConsole(startParameters(), &errorMessage) :
693 launchCDB(startParameters(), &errorMessage);
695 qDebug("<setupEngine ok=%d", ok);
697 showMessage(errorMessage, LogError);
698 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
699 notifyEngineSetupFailed();
703 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
706 qDebug("launchCDB startMode=%d", sp.startMode);
707 const QChar blank(QLatin1Char(' '));
708 // Start engine which will run until initial breakpoint:
709 // Determine binary (force MSVC), extension lib name and path to use
710 // The extension is passed as relative name with the path variable set
711 //(does not work with absolute path names)
712 const QString executable = cdbBinary(sp);
713 if (executable.isEmpty()) {
714 *errorMessage = tr("There is no CDB executable specified.");
720 Utils::winIs64BitBinary(executable);
724 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
725 if (!extensionFi.isFile()) {
726 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
727 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
730 const QString extensionFileName = extensionFi.fileName();
732 QStringList arguments;
733 const bool isRemote = sp.startMode == AttachToRemote;
734 if (isRemote) { // Must be first
735 arguments << QLatin1String("-remote") << sp.remoteChannel;
737 arguments << (QLatin1String("-a") + extensionFileName);
739 // Source line info/No terminal breakpoint / Pull extension
740 arguments << QLatin1String("-lines") << QLatin1String("-G")
741 // register idle (debuggee stop) notification
742 << QLatin1String("-c")
743 << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
744 if (sp.useTerminal) // Separate console
745 arguments << QLatin1String("-2");
746 if (!m_options->symbolPaths.isEmpty())
747 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
748 if (!m_options->sourcePaths.isEmpty())
749 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
750 // Compile argument string preserving quotes
751 QString nativeArguments = m_options->additionalArguments;
752 switch (sp.startMode) {
755 if (!nativeArguments.isEmpty())
756 nativeArguments.push_back(blank);
757 nativeArguments += QDir::toNativeSeparators(sp.executable);
762 case AttachCrashedExternal:
763 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
764 if (sp.startMode == AttachCrashedExternal) {
765 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
767 if (isCreatorConsole(startParameters(), *m_options))
768 arguments << QLatin1String("-pr") << QLatin1String("-pb");
772 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
775 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
776 if (!nativeArguments.isEmpty())
777 nativeArguments.push_back(blank);
778 nativeArguments += sp.processArgs;
781 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
782 arg(QDir::toNativeSeparators(executable),
783 arguments.join(QString(blank)) + blank + nativeArguments,
784 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
785 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
786 showMessage(msg, LogMisc);
788 m_outputBuffer.clear();
789 const QStringList environment = sp.environment.size() == 0 ?
790 QProcessEnvironment::systemEnvironment().toStringList() :
791 sp.environment.toStringList();
792 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
793 if (!sp.workingDirectory.isEmpty())
794 m_process.setWorkingDirectory(sp.workingDirectory);
797 if (!nativeArguments.isEmpty()) // Appends
798 m_process.setNativeArguments(nativeArguments);
800 m_process.start(executable, arguments);
801 if (!m_process.waitForStarted()) {
802 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
803 arg(QDir::toNativeSeparators(executable), m_process.errorString());
807 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
809 const unsigned long pid = 0;
811 showMessage(QString::fromLatin1("%1 running as %2").
812 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
813 m_hasDebuggee = true;
814 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
816 const QByteArray loadCommand = QByteArray(".load ")
817 + extensionFileName.toLocal8Bit();
818 postCommand(loadCommand, 0);
819 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
820 notifyEngineSetupOk();
825 void CdbEngine::setupInferior()
828 qDebug("setupInferior");
829 attemptBreakpointSynchronization();
831 if (startParameters().breakOnMain) {
832 const BreakpointParameters bp(BreakpointAtMain);
833 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings,
834 BreakpointId(-1), true), 0);
836 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
837 postCommand(".asm source_line", 0); // Source line in assembly
838 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
841 void CdbEngine::runEngine()
845 foreach (const QString &breakEvent, m_options->breakEvents)
846 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
850 bool CdbEngine::commandsPending() const
852 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
855 void CdbEngine::shutdownInferior()
858 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
859 isCdbProcessRunning());
861 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
863 qDebug("notifyInferiorShutdownOk");
864 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
865 notifyInferiorShutdownOk();
870 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
872 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
873 notifyInferiorShutdownOk();
875 // A command got stuck.
876 if (commandsPending()) {
877 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
878 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
879 notifyInferiorShutdownFailed();
882 if (!canInterruptInferior()) {
883 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
884 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
885 notifyInferiorShutdownFailed();
888 interruptInferior(); // Calls us again
892 /* shutdownEngine/processFinished:
893 * Note that in the case of launching a process by the debugger, the debugger
894 * automatically quits a short time after reporting the session becoming
895 * inaccessible without debuggee (notifyInferiorExited). In that case,
896 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
897 * as not to confuse the state engine.
900 void CdbEngine::shutdownEngine()
903 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
904 "accessible=%d,commands pending=%d",
905 stateName(state()), isCdbProcessRunning(), m_accessible,
908 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
909 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
910 notifyEngineShutdownOk();
914 // No longer trigger anything from messages
915 m_ignoreCdbOutput = true;
916 // Go for kill if there are commands pending.
917 if (m_accessible && !commandsPending()) {
918 // detach: Wait for debugger to finish.
919 if (m_effectiveStartMode == AttachExternal)
921 // Remote requires a bit more force to quit.
922 if (m_effectiveStartMode == AttachToRemote) {
923 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
924 postCommand("qq", 0);
928 m_notifyEngineShutdownOnTermination = true;
931 // Remote process. No can do, currently
932 m_notifyEngineShutdownOnTermination = true;
933 Utils::SynchronousProcess::stopProcess(m_process);
936 // Lost debuggee, debugger should quit anytime now
937 if (!m_hasDebuggee) {
938 m_notifyEngineShutdownOnTermination = true;
944 void CdbEngine::processFinished()
947 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
948 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
949 m_process.exitStatus(), m_process.exitCode());
951 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
953 showMessage(tr("CDB crashed"), LogError); // not in your life.
955 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
958 if (m_notifyEngineShutdownOnTermination) {
961 qDebug("notifyEngineIll");
962 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
965 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
966 notifyEngineShutdownOk();
969 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
970 // Otherwise, we take a shortcut.
971 if (isSlaveEngine()) {
972 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
973 notifyInferiorExited();
975 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
976 notifyEngineSpontaneousShutdown();
981 void CdbEngine::detachDebugger()
983 postCommand(".detach", 0);
986 static inline bool isWatchIName(const QByteArray &iname)
988 return iname.startsWith("watch");
991 void CdbEngine::updateWatchData(const WatchData &dataIn,
992 const WatchUpdateFlags & flags)
994 if (debug || debugLocals || debugWatches)
995 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
996 elapsedLogTime(), m_accessible, stateName(state()),
997 flags.tryIncremental,
998 qPrintable(dataIn.toString()));
1000 if (!m_accessible) // Add watch data while running?
1004 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
1006 ByteArrayInputStream str(args);
1007 str << dataIn.iname << " \"" << dataIn.exp << '"';
1008 postExtensionCommand("addwatch", args, 0,
1009 &CdbEngine::handleAddWatch, 0,
1010 qVariantFromValue(dataIn));
1014 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
1015 WatchData data = dataIn;
1016 data.setAllUnneeded();
1017 watchHandler()->insertData(data);
1020 updateLocalVariable(dataIn.iname);
1023 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1025 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1027 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1028 if (reply->success) {
1029 updateLocalVariable(item.iname);
1031 item.setError(tr("Unable to add expression"));
1032 watchHandler()->insertData(item);
1033 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1034 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1035 reply->errorMessage), LogError);
1039 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1041 if (debuggerCore()->boolSetting(VerboseLog))
1042 str << blankSeparator << "-v";
1043 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1044 str << blankSeparator << "-c";
1045 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1046 if (!typeFormats.isEmpty())
1047 str << blankSeparator << "-T " << typeFormats;
1048 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1049 if (!individualFormats.isEmpty())
1050 str << blankSeparator << "-I " << individualFormats;
1053 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1055 const bool isWatch = isWatchIName(iname);
1057 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1058 QByteArray localsArguments;
1059 ByteArrayInputStream str(localsArguments);
1060 addLocalsOptions(str);
1062 const int stackFrame = stackHandler()->currentIndex();
1063 if (stackFrame < 0) {
1064 qWarning("Internal error; no stack frame in updateLocalVariable");
1067 str << blankSeparator << stackFrame;
1069 str << blankSeparator << iname;
1070 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1073 unsigned CdbEngine::debuggerCapabilities() const
1075 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1076 |WatchpointByAddressCapability|JumpToLineCapability|AddWatcherCapability
1077 |ReloadModuleCapability
1078 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1079 |BreakConditionCapability|TracePointCapability
1080 |BreakModuleCapability
1081 |OperateByInstructionCapability
1082 |RunToLineCapability;
1085 void CdbEngine::executeStep()
1087 if (!m_operateByInstruction)
1088 m_sourceStepInto = true; // See explanation at handleStackTrace().
1089 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1090 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1091 notifyInferiorRunRequested();
1094 void CdbEngine::executeStepOut()
1096 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1097 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1098 notifyInferiorRunRequested();
1101 void CdbEngine::executeNext()
1103 postCommand(QByteArray("p"), 0); // Step over -> p
1104 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1105 notifyInferiorRunRequested();
1108 void CdbEngine::executeStepI()
1113 void CdbEngine::executeNextI()
1118 void CdbEngine::continueInferior()
1120 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1121 notifyInferiorRunRequested();
1122 doContinueInferior();
1125 void CdbEngine::doContinueInferior()
1127 postCommand(QByteArray("g"), 0);
1130 bool CdbEngine::canInterruptInferior() const
1132 return m_effectiveStartMode != AttachToRemote && inferiorPid();
1135 void CdbEngine::interruptInferior()
1138 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1139 if (canInterruptInferior()) {
1140 doInterruptInferior(NoSpecialStop);
1142 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1143 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1144 notifyInferiorStopOk();
1145 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1146 notifyInferiorRunRequested();
1147 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1148 notifyInferiorRunOk();
1152 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1154 if (m_specialStopMode == NoSpecialStop)
1155 doInterruptInferior(CustomSpecialStop);
1156 m_customSpecialStopData.push_back(v);
1159 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1162 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1163 m_specialStopMode = sm;
1164 QString errorMessage;
1165 showMessage(QString::fromLatin1("Interrupting process %1...").arg(inferiorPid()), LogMisc);
1166 if (!winDebugBreakProcess(inferiorPid(), &errorMessage)) {
1167 m_specialStopMode = oldSpecialMode;
1168 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(inferiorPid()).arg(errorMessage), LogError);
1175 void CdbEngine::executeRunToLine(const ContextData &data)
1177 // Add one-shot breakpoint
1178 BreakpointParameters bp;
1180 bp.type =BreakpointByAddress;
1181 bp.address = data.address;
1183 bp.type =BreakpointByFileAndLine;
1184 bp.fileName = data.fileName;
1185 bp.lineNumber = data.lineNumber;
1187 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1191 void CdbEngine::executeRunToFunction(const QString &functionName)
1193 // Add one-shot breakpoint
1194 BreakpointParameters bp(BreakpointByFunction);
1195 bp.functionName = functionName;
1197 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1201 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1203 const Registers registers = registerHandler()->registers();
1204 QTC_ASSERT(regnr < registers.size(), return)
1205 // Value is decimal or 0x-hex-prefixed
1207 ByteArrayInputStream str(cmd);
1208 str << "r " << registers.at(regnr).name << '=' << value;
1209 postCommand(cmd, 0);
1213 void CdbEngine::executeJumpToLine(const ContextData &data)
1216 // Goto address directly.
1217 jumpToAddress(data.address);
1218 gotoLocation(Location(data.address));
1220 // Jump to source line: Resolve source line address and go to that location
1222 ByteArrayInputStream str(cmd);
1223 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1224 const QVariant cookie = qVariantFromValue(data);
1225 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1229 void CdbEngine::jumpToAddress(quint64 address)
1231 // Fake a jump to address by setting the PC register.
1232 QByteArray registerCmd;
1233 ByteArrayInputStream str(registerCmd);
1234 // PC-register depending on 64/32bit.
1235 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1236 str.setHexPrefix(true);
1237 str.setIntegerBase(16);
1239 postCommand(registerCmd, 0);
1242 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1244 if (cmd->reply.isEmpty())
1246 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1247 // Set register 'rip' to hex address and goto lcoation
1248 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1249 const int equalPos = answer.indexOf(" = ");
1252 answer.remove(0, equalPos + 3);
1253 answer.remove(QLatin1Char('`'));
1255 const quint64 address = answer.toLongLong(&ok, 16);
1256 if (ok && address) {
1257 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1258 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1259 jumpToAddress(address);
1260 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1264 static inline bool isAsciiWord(const QString &s)
1266 foreach (const QChar &c, s) {
1267 if (!c.isLetterOrNumber() || c.toAscii() == 0)
1273 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1276 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1278 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1279 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1283 ByteArrayInputStream str(cmd);
1284 switch (value.type()) {
1285 case QVariant::String: {
1286 // Convert qstring to Utf16 data not considering endianness for Windows.
1287 const QString s = value.toString();
1288 if (isAsciiWord(s)) {
1289 str << m_extensionCommandPrefixBA << "assign \"" << w->iname << '='
1290 << s.toLatin1() << '"';
1292 const QByteArray utf16(reinterpret_cast<const char *>(s.utf16()), 2 * s.size());
1293 str << m_extensionCommandPrefixBA << "assign -u " << w->iname << '='
1299 str << m_extensionCommandPrefixBA << "assign " << w->iname << '='
1300 << value.toString();
1304 postCommand(cmd, 0);
1305 // Update all locals in case we change a union or something pointed to
1306 // that affects other variables, too.
1310 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1312 int currentThreadId;
1313 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1314 threadsHandler()->setThreads(threads);
1315 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1316 forceCurrentThreadId : currentThreadId);
1319 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1322 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1323 if (reply->success) {
1325 data.fromString(reply->reply);
1327 // Continue sequence
1328 postCommandSequence(reply->commandSequence);
1330 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1334 void CdbEngine::executeDebuggerCommand(const QString &command)
1336 postCommand(command.toLocal8Bit(), QuietCommand);
1339 // Post command without callback
1340 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1343 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1344 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1345 if (!(flags & QuietCommand))
1346 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1347 m_process.write(cmd + '\n');
1350 // Post a built-in-command producing free-format output with a callback.
1351 // In order to catch the output, it is enclosed in 'echo' commands
1352 // printing a specially formatted token to be identifiable in the output.
1353 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1354 BuiltinCommandHandler handler,
1355 unsigned nextCommandFlag,
1356 const QVariant &cookie)
1358 if (!m_accessible) {
1359 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1360 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1361 showMessage(msg, LogError);
1364 if (!flags & QuietCommand)
1365 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1367 const int token = m_nextCommandToken++;
1368 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1370 m_builtinCommandQueue.push_back(pendingCommand);
1371 // Enclose command in echo-commands for token
1373 ByteArrayInputStream str(fullCmd);
1374 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1375 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1377 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1378 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1379 m_builtinCommandQueue.size(), nextCommandFlag);
1381 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1382 fullCmd.constData());
1383 m_process.write(fullCmd);
1386 // Post an extension command producing one-line output with a callback,
1387 // pass along token for identification in queue.
1388 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1389 const QByteArray &arguments,
1391 ExtensionCommandHandler handler,
1392 unsigned nextCommandFlag,
1393 const QVariant &cookie)
1395 if (!m_accessible) {
1396 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1397 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1398 showMessage(msg, LogError);
1402 const int token = m_nextCommandToken++;
1404 // Format full command with token to be recognizeable in the output
1406 ByteArrayInputStream str(fullCmd);
1407 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1408 if (!arguments.isEmpty())
1409 str << ' ' << arguments;
1411 if (!flags & QuietCommand)
1412 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1414 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1416 m_extensionCommandQueue.push_back(pendingCommand);
1417 // Enclose command in echo-commands for token
1419 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1420 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1421 m_extensionCommandQueue.size(), nextCommandFlag);
1422 m_process.write(fullCmd + '\n');
1425 void CdbEngine::activateFrame(int index)
1427 // TODO: assembler,etc
1430 const StackFrames &frames = stackHandler()->frames();
1431 QTC_ASSERT(index < frames.size(), return; )
1433 const StackFrame frame = frames.at(index);
1434 if (debug || debugLocals)
1435 qDebug("activateFrame idx=%d '%s' %d", index,
1436 qPrintable(frame.file), frame.line);
1437 stackHandler()->setCurrentIndex(index);
1438 const bool showAssembler = !frames.at(index).isUsable();
1439 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1440 watchHandler()->beginCycle();
1441 watchHandler()->endCycle();
1442 QAction *assemblerAction = theAssemblerAction();
1443 if (assemblerAction->isChecked()) {
1444 gotoLocation(frame);
1446 assemblerAction->trigger(); // Seems to trigger update
1449 gotoLocation(frame);
1454 void CdbEngine::updateLocals(bool forNewStackFrame)
1456 typedef QHash<QByteArray, int> WatcherHash;
1458 const int frameIndex = stackHandler()->currentIndex();
1459 if (frameIndex < 0) {
1460 watchHandler()->beginCycle();
1461 watchHandler()->endCycle();
1464 const StackFrame frame = stackHandler()->currentFrame();
1465 if (!frame.isUsable()) {
1466 watchHandler()->beginCycle();
1467 watchHandler()->endCycle();
1470 /* Watchers: Forcibly discard old symbol group as switching from
1471 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1472 * and cause errors as it seems to go 'stale' when switching threads.
1473 * Initial expand, get uninitialized and query */
1474 QByteArray arguments;
1475 ByteArrayInputStream str(arguments);
1478 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1479 if (!expanded.isEmpty()) {
1480 str << blankSeparator << "-e ";
1482 foreach(const QByteArray &e, expanded) {
1488 addLocalsOptions(str);
1489 // Uninitialized variables if desired. Quote as safeguard against shadowed
1490 // variables in case of errors in uninitializedVariables().
1491 if (debuggerCore()->boolSetting(UseCodeModel)) {
1492 QStringList uninitializedVariables;
1493 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1494 frame.function, frame.file, frame.line, &uninitializedVariables);
1495 if (!uninitializedVariables.isEmpty()) {
1496 str << blankSeparator << "-u \"";
1498 foreach(const QString &u, uninitializedVariables) {
1501 str << localsPrefixC << u;
1506 // Perform watches synchronization
1507 str << blankSeparator << "-W";
1508 const WatcherHash watcherHash = WatchHandler::watcherNames();
1509 if (!watcherHash.isEmpty()) {
1510 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1511 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1512 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1516 // Required arguments: frame
1517 str << blankSeparator << frameIndex;
1518 watchHandler()->beginCycle();
1519 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1522 void CdbEngine::selectThread(int index)
1524 if (index < 0 || index == threadsHandler()->currentThread())
1528 const int newThreadId = threadsHandler()->threads().at(index).id;
1529 threadsHandler()->setCurrentThread(index);
1531 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1532 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1535 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1537 QTC_ASSERT(m_accessible, return;)
1539 ByteArrayInputStream str(cmd);
1540 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1541 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1542 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1545 // Parse: "00000000`77606060 cc int 3"
1546 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1548 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1549 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1550 agent->setContents(parseCdbDisassembler(command->reply));
1553 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1556 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1557 const MemoryViewCookie cookie(agent, editor, addr, length);
1559 postFetchMemory(cookie);
1561 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1565 void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
1568 ByteArrayInputStream str(args);
1569 str << cookie.address << ' ' << cookie.length;
1570 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0,
1571 qVariantFromValue(cookie));
1574 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1576 QTC_ASSERT(!data.isEmpty(), return; )
1577 if (!m_accessible) {
1578 const MemoryChangeCookie cookie(addr, data);
1579 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1581 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1585 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1587 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1588 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1589 if (command->success) {
1590 const QByteArray data = QByteArray::fromBase64(command->reply);
1591 if (unsigned(data.size()) == memViewCookie.length)
1592 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1593 memViewCookie.address, data);
1595 showMessage(QString::fromLocal8Bit(command->errorMessage), LogWarning);
1599 void CdbEngine::reloadModules()
1601 postCommandSequence(CommandListModules);
1604 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1608 void CdbEngine::loadAllSymbols()
1612 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1614 Q_UNUSED(moduleName)
1617 void CdbEngine::reloadRegisters()
1619 postCommandSequence(CommandListRegisters);
1622 void CdbEngine::reloadSourceFiles()
1626 void CdbEngine::reloadFullStack()
1629 qDebug("%s", Q_FUNC_INFO);
1630 postCommandSequence(CommandListStack);
1633 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1635 if (reply->success) {
1636 notifyInferiorPid(reply->reply.toULongLong());
1637 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1638 notifyInferiorSetupOk();
1640 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1641 arg(QLatin1String(reply->errorMessage)), LogError);
1642 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1643 notifyInferiorSetupFailed();
1647 // Parse CDB gdbmi register syntax
1648 static inline Register parseRegister(const GdbMi &gdbmiReg)
1651 reg.name = gdbmiReg.findChild("name").data();
1652 const GdbMi description = gdbmiReg.findChild("description");
1653 if (description.type() != GdbMi::Invalid) {
1655 reg.name += description.data();
1658 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1662 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1664 if (reply->success) {
1666 value.fromString(reply->reply);
1667 if (value.type() == GdbMi::List) {
1669 modules.reserve(value.childCount());
1670 foreach (const GdbMi &gdbmiModule, value.children()) {
1672 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1673 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1674 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1675 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1676 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1677 module.symbolsRead = Module::ReadOk;
1678 modules.push_back(module);
1680 modulesHandler()->setModules(modules);
1682 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1683 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1686 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1687 arg(QLatin1String(reply->errorMessage)), LogError);
1689 postCommandSequence(reply->commandSequence);
1693 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1695 if (reply->success) {
1697 value.fromString(reply->reply);
1698 if (value.type() == GdbMi::List) {
1699 Registers registers;
1700 registers.reserve(value.childCount());
1701 foreach (const GdbMi &gdbmiReg, value.children())
1702 registers.push_back(parseRegister(gdbmiReg));
1703 registerHandler()->setAndMarkRegisters(registers);
1705 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1706 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1709 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1710 arg(QLatin1String(reply->errorMessage)), LogError);
1712 postCommandSequence(reply->commandSequence);
1715 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1717 if (reply->success) {
1718 QList<WatchData> watchData;
1720 root.fromString(reply->reply);
1721 QTC_ASSERT(root.isList(), return ; )
1723 qDebug() << root.toString(true, 4);
1725 // Courtesy of GDB engine
1726 foreach (const GdbMi &child, root.children()) {
1728 dummy.iname = child.findChild("iname").data();
1729 dummy.name = QLatin1String(child.findChild("name").data());
1730 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1732 watchHandler()->insertBulkData(watchData);
1733 watchHandler()->endCycle();
1735 QDebug nsp = qDebug().nospace();
1736 nsp << "Obtained " << watchData.size() << " items:\n";
1737 foreach (const WatchData &wd, watchData)
1738 nsp << wd.toString() <<'\n';
1740 const bool forNewStackFrame = reply->cookie.toBool();
1741 if (forNewStackFrame)
1742 emit stackFrameCompleted();
1744 showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1748 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1750 if (!reply->success)
1751 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1754 enum CdbExecutionStatus {
1755 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1756 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1757 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1758 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1759 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1760 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1761 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1763 static const char *cdbStatusName(unsigned long s)
1766 case CDB_STATUS_NO_CHANGE:
1770 case CDB_STATUS_GO_HANDLED:
1771 return "go_handled";
1772 case CDB_STATUS_GO_NOT_HANDLED:
1773 return "go_not_handled";
1774 case CDB_STATUS_STEP_OVER:
1776 case CDB_STATUS_STEP_INTO:
1778 case CDB_STATUS_BREAK:
1780 case CDB_STATUS_NO_DEBUGGEE:
1781 return "no_debuggee";
1782 case CDB_STATUS_STEP_BRANCH:
1783 return "step_branch";
1784 case CDB_STATUS_IGNORE_EVENT:
1785 return "ignore_event";
1786 case CDB_STATUS_RESTART_REQUESTED:
1787 return "restart_requested";
1788 case CDB_STATUS_REVERSE_GO:
1789 return "reverse_go";
1790 case CDB_STATUS_REVERSE_STEP_BRANCH:
1791 return "reverse_step_branch";
1792 case CDB_STATUS_REVERSE_STEP_OVER:
1793 return "reverse_step_over";
1794 case CDB_STATUS_REVERSE_STEP_INTO:
1795 return "reverse_step_into";
1800 /* Examine how to react to a stop. */
1801 enum StopActionFlags
1804 StopReportLog = 0x1,
1805 StopReportStatusMessage = 0x2,
1806 StopReportParseError = 0x4,
1807 StopShowExceptionMessageBox = 0x8,
1808 // Notify stop or just continue
1809 StopNotifyStop = 0x10,
1810 StopIgnoreContinue = 0x20,
1811 // Hit on break in artificial stop thread (created by DebugBreak()).
1812 StopInArtificialThread = 0x40,
1813 StopShutdownInProgress = 0x80 // Shutdown in progress
1816 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1817 const QString &threadId)
1819 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1820 .arg(id.toString()).arg(number).arg(threadId);
1823 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1824 const QByteArray &condition,
1825 const QString &threadId)
1827 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1828 .arg(id.toString()).arg(number).arg(threadId, QString::fromAscii(condition));
1831 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1833 QString *exceptionBoxMessage,
1834 bool conditionalBreakPointTriggered)
1836 // Report stop reason (GDBMI)
1838 if (targetState() == DebuggerFinished)
1839 rc |= StopShutdownInProgress;
1841 qDebug("%s", stopReason.toString(true, 4).constData());
1842 const QByteArray reason = stopReason.findChild("reason").data();
1843 if (reason.isEmpty()) {
1844 *message = tr("Malformed stop response received.");
1845 rc |= StopReportParseError|StopNotifyStop;
1848 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1849 if (state() == InferiorStopOk) {
1850 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1851 arg(QString::fromAscii(reason));
1852 rc |= StopReportLog;
1855 const int threadId = stopReason.findChild("threadId").data().toInt();
1856 if (reason == "breakpoint") {
1857 // Note: Internal breakpoints (run to line) are reported with id=0.
1858 // Step out creates temporary breakpoints with id 10000.
1861 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1862 if (breakpointIdG.isValid()) {
1863 id = BreakpointId(breakpointIdG.data().toInt());
1864 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1865 const BreakpointResponse parameters = breakHandler()->response(id);
1866 // Trace point? Just report.
1867 number = parameters.id.majorPart();
1868 if (parameters.tracepoint) {
1869 *message = msgTracePointTriggered(id, number, QString::number(threadId));
1870 return StopReportLog|StopIgnoreContinue;
1872 // Trigger evaluation of BP expression unless we are already in the response.
1873 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1874 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1875 QString::number(threadId));
1876 ConditionalBreakPointCookie cookie(id);
1877 cookie.stopReason = stopReason;
1878 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1879 return StopReportLog;
1882 id = BreakpointId();
1885 QString tid = QString::number(threadId);
1886 if (id && breakHandler()->type(id) == WatchpointAtAddress) {
1887 *message = msgWatchpointByAddressTriggered(id, number, breakHandler()->address(id), tid);
1888 } else if (id && breakHandler()->type(id) == WatchpointAtExpression) {
1889 *message = msgWatchpointByExpressionTriggered(id, number, breakHandler()->expression(id), tid);
1891 *message = msgBreakpointTriggered(id, number, tid);
1893 rc |= StopReportStatusMessage|StopNotifyStop;
1896 if (reason == "exception") {
1897 WinException exception;
1898 exception.fromGdbMI(stopReason);
1899 QString description = exception.toString();
1901 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1902 // pulls DLLs. Avoid showing a 'stopped' Message box.
1903 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1904 || exception.exceptionCode == winExceptionWX86Breakpoint)
1905 return StopNotifyStop;
1906 if (exception.exceptionCode == winExceptionCtrlPressed) {
1907 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1908 *message = msgInterrupted();
1909 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1912 if (isDebuggerWinException(exception.exceptionCode)) {
1913 rc |= StopReportStatusMessage|StopNotifyStop;
1914 // Detect interruption by DebugBreak() and force a switch to thread 0.
1915 if (exception.function == "ntdll!DbgBreakPoint")
1916 rc |= StopInArtificialThread;
1917 *message = msgInterrupted();
1921 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1922 *message = description;
1923 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1926 *message = msgStopped(QLatin1String(reason));
1927 rc |= StopReportStatusMessage|StopNotifyStop;
1931 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1937 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1938 elapsedLogTime(), messageBA.constData(),
1939 stateName(state()), m_specialStopMode);
1941 // Switch source level debugging
1942 syncOperateByInstruction(m_operateByInstructionPending);
1944 // Engine-special stop reasons: Breakpoints and setup
1945 const SpecialStopMode specialStopMode = m_specialStopMode;
1947 m_specialStopMode = NoSpecialStop;
1949 switch(specialStopMode) {
1950 case SpecialStopSynchronizeBreakpoints:
1952 qDebug("attemptBreakpointSynchronization in special stop");
1953 attemptBreakpointSynchronization();
1954 doContinueInferior();
1956 case SpecialStopGetWidgetAt:
1957 postWidgetAtCommand();
1959 case CustomSpecialStop:
1960 foreach (const QVariant &data, m_customSpecialStopData)
1961 handleCustomSpecialStop(data);
1962 m_customSpecialStopData.clear();
1963 doContinueInferior();
1969 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1970 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1971 notifyEngineSetupOk();
1975 stopReason.fromString(messageBA);
1976 processStop(stopReason, false);
1979 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1981 // Further examine stop and report to user
1983 QString exceptionBoxMessage;
1984 int forcedThreadId = -1;
1985 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1986 conditionalBreakPointTriggered);
1987 // Do the non-blocking log reporting
1988 if (stopFlags & StopReportLog)
1989 showMessage(message, LogMisc);
1990 if (stopFlags & StopReportStatusMessage)
1991 showStatusMessage(message);
1992 if (stopFlags & StopReportParseError)
1993 showMessage(message, LogError);
1994 // Ignore things like WOW64, report tracepoints.
1995 if (stopFlags & StopIgnoreContinue) {
1996 postCommand("g", 0);
1999 // Notify about state and send off command sequence to get stack, etc.
2000 if (stopFlags & StopNotifyStop) {
2001 if (state() == InferiorStopRequested) {
2002 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
2003 notifyInferiorStopOk();
2005 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
2006 notifyInferiorSpontaneousStop();
2008 // Prevent further commands from being sent if shutdown is in progress
2009 if (stopFlags & StopShutdownInProgress) {
2010 showMessage(QString::fromLatin1("Shutdown request detected..."));
2013 const bool sourceStepInto = m_sourceStepInto;
2014 m_sourceStepInto = false;
2015 // Start sequence to get all relevant data.
2016 if (stopFlags & StopInArtificialThread) {
2017 showMessage(tr("Switching to main thread..."), LogMisc);
2018 postCommand("~0 s", 0);
2020 // Re-fetch stack again.
2021 postCommandSequence(CommandListStack);
2023 const GdbMi stack = stopReason.findChild("stack");
2024 if (stack.isValid()) {
2025 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
2026 executeStep(); // Hit on a frame while step into, see parseStackTrace().
2030 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
2033 const GdbMi threads = stopReason.findChild("threads");
2034 if (threads.isValid()) {
2035 parseThreads(threads, forcedThreadId);
2037 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
2039 // Fire off remaining commands asynchronously
2040 if (!m_pendingBreakpointMap.isEmpty())
2041 postCommandSequence(CommandListBreakPoints);
2042 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
2043 postCommandSequence(CommandListRegisters);
2044 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2045 postCommandSequence(CommandListModules);
2047 // After the sequence has been sent off and CDB is pondering the commands,
2048 // pop up a message box for exceptions.
2049 if (stopFlags & StopShowExceptionMessageBox)
2050 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2053 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2055 const DebuggerState s = state();
2056 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2060 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2061 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2064 case EngineShutdownRequested:
2067 case InferiorShutdownRequested:
2075 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2077 const DebuggerState s = state();
2080 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2084 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2085 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2088 case EngineSetupRequested:
2090 case EngineRunRequested:
2091 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2092 notifyEngineRunAndInferiorRunOk();
2095 case InferiorStopOk:
2096 // Inaccessible without debuggee (exit breakpoint)
2097 // We go for spontaneous engine shutdown instead.
2098 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2100 qDebug("Lost debuggeee");
2101 m_hasDebuggee = false;
2104 case InferiorRunRequested:
2105 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2106 notifyInferiorRunOk();
2109 case EngineShutdownRequested:
2116 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2119 QDebug nospace = qDebug().nospace();
2120 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2121 << ' ' << stateName(state());
2122 if (t == 'N' || debug > 1) {
2123 nospace << ' ' << message;
2125 nospace << ' ' << message.size() << " bytes";
2129 // Is there a reply expected, some command queued?
2130 if (t == 'R' || t == 'N') {
2131 if (token == -1) { // Default token, user typed in extension command
2132 showMessage(QString::fromLatin1(message), LogMisc);
2135 const int index = indexOfCommand(m_extensionCommandQueue, token);
2137 // Did the command finish? Take off queue and complete, invoke CB
2138 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2140 command->success = true;
2141 command->reply = message;
2143 command->success = false;
2144 command->errorMessage = message;
2147 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2148 command->command.constData(), command->token, m_extensionCommandQueue.size());
2149 if (command->handler)
2150 (this->*(command->handler))(command);
2155 if (what == "debuggee_output") {
2156 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2160 if (what == "event") {
2161 showStatusMessage(QString::fromAscii(message), 5000);
2165 if (what == "session_accessible") {
2166 if (!m_accessible) {
2167 m_accessible = true;
2168 handleSessionAccessible(message.toULong());
2173 if (what == "session_inaccessible") {
2175 m_accessible = false;
2176 handleSessionInaccessible(message.toULong());
2181 if (what == "session_idle") {
2182 handleSessionIdle(message);
2186 if (what == "exception") {
2187 WinException exception;
2189 gdbmi.fromString(message);
2190 exception.fromGdbMI(gdbmi);
2191 const QString message = exception.toString(true);
2192 showStatusMessage(message);
2193 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2194 if (exception.exceptionCode == winExceptionCppException)
2195 showMessage(message + QLatin1Char('\n'), AppOutput);
2203 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2204 enum { CdbPromptLength = 7 };
2206 static inline bool isCdbPrompt(const QByteArray &c)
2208 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2209 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2210 && std::isdigit(c.at(4));
2213 // Check for '<token>32>' or '<token>32<'
2214 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2215 int *token, bool *isStart)
2219 const int tokenPrefixSize = tokenPrefix.size();
2220 const int size = c.size();
2221 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2223 switch (c.at(size - 1)) {
2233 if (!c.startsWith(tokenPrefix))
2236 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2240 void CdbEngine::parseOutputLine(QByteArray line)
2242 // The hooked output callback in the extension suppresses prompts,
2243 // it should happen only in initial and exit stages. Note however that
2244 // if the output is not hooked, sequences of prompts are possible which
2245 // can mix things up.
2246 while (isCdbPrompt(line))
2247 line.remove(0, CdbPromptLength);
2248 // An extension notification (potentially consisting of several chunks)
2249 if (line.startsWith(m_creatorExtPrefix)) {
2250 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2251 const char type = line.at(m_creatorExtPrefix.size());
2253 const int tokenPos = m_creatorExtPrefix.size() + 2;
2254 const int tokenEndPos = line.indexOf('|', tokenPos);
2255 QTC_ASSERT(tokenEndPos != -1, return)
2256 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2258 const int remainingChunksPos = tokenEndPos + 1;
2259 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2260 QTC_ASSERT(remainingChunksEndPos != -1, return)
2261 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2262 // const char 'serviceName'
2263 const int whatPos = remainingChunksEndPos + 1;
2264 const int whatEndPos = line.indexOf('|', whatPos);
2265 QTC_ASSERT(whatEndPos != -1, return)
2266 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2267 // Build up buffer, call handler once last chunk was encountered
2268 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2269 if (remainingChunks == 0) {
2270 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2271 m_extensionMessageBuffer.clear();
2275 // Check for command start/end tokens within which the builtin command
2276 // output is enclosed
2278 bool isStartToken = false;
2279 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2281 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2282 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2284 // If there is a current command, wait for end of output indicated by token,
2285 // command, trigger handler and finish, else append to its output.
2286 if (m_currentBuiltinCommandIndex != -1) {
2287 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2288 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2289 if (isCommandToken) {
2290 // Did the command finish? Invoke callback and remove from queue.
2292 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2293 currentCommand->command.constData(), currentCommand->token,
2294 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2295 QTC_ASSERT(token == currentCommand->token, return; );
2296 if (currentCommand->handler)
2297 (this->*(currentCommand->handler))(currentCommand);
2298 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2299 m_currentBuiltinCommandIndex = -1;
2301 // Record output of current command
2302 currentCommand->reply.push_back(line);
2305 } // m_currentCommandIndex
2306 if (isCommandToken) {
2307 // Beginning command token encountered, start to record output.
2308 const int index = indexOfCommand(m_builtinCommandQueue, token);
2309 QTC_ASSERT(isStartToken && index != -1, return; );
2310 m_currentBuiltinCommandIndex = index;
2311 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2313 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2317 showMessage(QString::fromLocal8Bit(line), LogMisc);
2320 void CdbEngine::readyReadStandardOut()
2322 if (m_ignoreCdbOutput)
2324 m_outputBuffer += m_process.readAllStandardOutput();
2325 // Split into lines and parse line by line.
2327 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2328 if (endOfLinePos == -1) {
2332 QByteArray line = m_outputBuffer.left(endOfLinePos);
2333 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2334 line.truncate(line.size() - 1);
2335 parseOutputLine(line);
2336 m_outputBuffer.remove(0, endOfLinePos + 1);
2341 void CdbEngine::readyReadStandardError()
2343 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2346 void CdbEngine::processError()
2348 showMessage(m_process.errorString(), LogError);
2352 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2353 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2355 QByteArray cmd(cmdC);
2356 ByteArrayInputStream str(cmd);
2357 foreach(const BreakpointData *bp, bps)
2358 str << ' ' << bp->bpNumber;
2363 bool CdbEngine::stateAcceptsBreakpointChanges() const
2367 case InferiorStopOk:
2375 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2377 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2378 if (!DebuggerEngine::isCppBreakpoint(data))
2380 switch (data.type) {
2382 case BreakpointAtFork:
2383 case WatchpointAtExpression:
2384 case BreakpointAtSysCall:
2386 case WatchpointAtAddress:
2387 case BreakpointByFileAndLine:
2388 case BreakpointByFunction:
2389 case BreakpointByAddress:
2390 case BreakpointAtThrow:
2391 case BreakpointAtCatch:
2392 case BreakpointAtMain:
2393 case BreakpointAtExec:
2399 // Context for fixing file/line-type breakpoints, for delayed creation.
2400 class BreakpointCorrectionContext
2403 explicit BreakpointCorrectionContext(const CPlusPlus::Snapshot &s,
2404 const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy) :
2405 m_snapshot(s), m_workingCopy(workingCopy) {}
2407 unsigned fixLineNumber(const QString &fileName, unsigned lineNumber) const;
2410 const CPlusPlus::Snapshot m_snapshot;
2411 CPlusPlus::CppModelManagerInterface::WorkingCopy m_workingCopy;
2414 static CPlusPlus::Document::Ptr getParsedDocument(const QString &fileName,
2415 const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy,
2416 const CPlusPlus::Snapshot &snapshot)
2419 if (workingCopy.contains(fileName)) {
2420 src = workingCopy.source(fileName);
2422 Utils::FileReader reader;
2423 if (reader.fetch(fileName)) // ### FIXME error reporting
2424 src = QString::fromLocal8Bit(reader.data()); // ### FIXME encoding
2427 QByteArray source = snapshot.preprocessedCode(src, fileName);
2429 CPlusPlus::Document::Ptr doc = snapshot.documentFromSource(source, fileName);
2434 unsigned BreakpointCorrectionContext::fixLineNumber(const QString &fileName,
2435 unsigned lineNumber) const
2437 CPlusPlus::Document::Ptr doc = m_snapshot.document(fileName);
2438 if (!doc || !doc->translationUnit()->ast())
2439 doc = getParsedDocument(fileName, m_workingCopy, m_snapshot);
2441 CPlusPlus::FindCdbBreakpoint findVisitor(doc->translationUnit());
2442 const unsigned correctedLine = findVisitor(lineNumber);
2443 if (!correctedLine) {
2444 qWarning("Unable to find breakpoint location for %s:%d",
2445 qPrintable(QDir::toNativeSeparators(fileName)), lineNumber);
2449 qDebug("Code model: Breakpoint line %u -> %u in %s",
2450 lineNumber, correctedLine, qPrintable(fileName));
2451 return correctedLine;
2454 void CdbEngine::attemptBreakpointSynchronization()
2459 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2460 // Check if there is anything to be done at all.
2461 BreakHandler *handler = breakHandler();
2462 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2463 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2464 if (acceptsBreakpoint(id))
2465 handler->setEngine(id, this);
2467 // Quick check: is there a need to change something? - Populate module cache
2468 bool changed = false;
2469 const BreakpointIds ids = handler->engineBreakpointIds(this);
2470 foreach (BreakpointId id, ids) {
2471 switch (handler->state(id)) {
2472 case BreakpointInsertRequested:
2473 case BreakpointRemoveRequested:
2474 case BreakpointChangeRequested:
2477 case BreakpointInserted: {
2478 // Collect the new modules matching the files.
2479 // In the future, that information should be obtained from the build system.
2480 const BreakpointParameters &data = handler->breakpointData(id);
2481 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2482 m_fileNameModuleHash.insert(data.fileName, data.module);
2490 if (debugBreakpoints)
2491 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2492 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2496 if (!m_accessible) {
2498 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2499 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2502 // Add/Change breakpoints and store pending ones in map, since
2503 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2504 // handleBreakPoints will the complete that information and set it on the break handler.
2505 bool addedChanged = false;
2506 QScopedPointer<BreakpointCorrectionContext> lineCorrection;
2507 foreach (BreakpointId id, ids) {
2508 BreakpointParameters parameters = handler->breakpointData(id);
2509 BreakpointResponse response;
2510 response.fromParameters(parameters);
2511 // If we encountered that file and have a module for it: Add it.
2512 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2513 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2514 if (it != m_fileNameModuleHash.constEnd())
2515 parameters.module = it.value();
2517 switch (handler->state(id)) {
2518 case BreakpointInsertRequested:
2519 if (parameters.type == BreakpointByFileAndLine) {
2520 if (lineCorrection.isNull())
2521 lineCorrection.reset(new BreakpointCorrectionContext(debuggerCore()->cppCodeModelSnapshot(),
2522 CPlusPlus::CppModelManagerInterface::instance()->workingCopy()));
2523 response.lineNumber = lineCorrection->fixLineNumber(parameters.fileName, parameters.lineNumber);
2524 postCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false), 0);
2526 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2528 if (!parameters.enabled)
2529 postCommand("bd " + QByteArray::number(id.majorPart()), 0);
2530 handler->notifyBreakpointInsertProceeding(id);
2531 handler->notifyBreakpointInsertOk(id);
2532 m_pendingBreakpointMap.insert(id, response);
2533 addedChanged = true;
2534 // Ensure enabled/disabled is correct in handler and line number is there.
2535 handler->setResponse(id, response);
2536 if (debugBreakpoints)
2537 qDebug("Adding %d %s\n", id.toInternalId(),
2538 qPrintable(response.toString()));
2540 case BreakpointChangeRequested:
2541 handler->notifyBreakpointChangeProceeding(id);
2542 if (debugBreakpoints)
2543 qDebug("Changing %d:\n %s\nTo %s\n", id.toInternalId(),
2544 qPrintable(handler->response(id).toString()),
2545 qPrintable(parameters.toString()));
2546 if (parameters.enabled != handler->response(id).enabled) {
2547 // Change enabled/disabled breakpoints without triggering update.
2548 postCommand((parameters.enabled ? "be " : "bd ")
2549 + QByteArray::number(id.majorPart()), 0);
2550 response.pending = false;
2551 response.enabled = parameters.enabled;
2552 handler->setResponse(id, response);
2554 // Delete and re-add, triggering update
2555 addedChanged = true;
2556 postCommand("bc " + QByteArray::number(id.majorPart()), 0);
2557 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2558 m_pendingBreakpointMap.insert(id, response);
2560 handler->notifyBreakpointChangeOk(id);
2562 case BreakpointRemoveRequested:
2563 postCommand("bc " + QByteArray::number(id.majorPart()), 0);
2564 handler->notifyBreakpointRemoveProceeding(id);
2565 handler->notifyBreakpointRemoveOk(id);
2566 m_pendingBreakpointMap.remove(id);
2572 // List breakpoints and send responses
2574 postCommandSequence(CommandListBreakPoints);
2577 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2578 // manager to correctly process it) and convert to clean path.
2579 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2582 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2583 if (it != m_normalizedFileCache.constEnd())
2585 if (debugSourceMapping)
2586 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2587 // Do we have source path mappings? ->Apply.
2588 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2590 // Up/lower case normalization according to Windows.
2592 QString normalized = Utils::normalizePathName(fileName);
2594 QString normalized = fileName;
2596 if (debugSourceMapping)
2597 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2598 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2599 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2600 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2602 // At least upper case drive letter if failed.
2603 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2604 result.fileName[0] = result.fileName.at(0).toUpper();
2606 m_normalizedFileCache.insert(f, result);
2607 if (debugSourceMapping)
2608 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2612 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2613 // has more processing.
2614 static StackFrames parseFrames(const GdbMi &gdbmi)
2617 const int count = gdbmi.childCount();
2619 for (int i = 0; i < count; i++) {
2620 const GdbMi &frameMi = gdbmi.childAt(i);
2623 const GdbMi fullName = frameMi.findChild("fullname");
2624 if (fullName.isValid()) {
2625 frame.file = QFile::decodeName(fullName.data());
2626 frame.line = frameMi.findChild("line").data().toInt();
2627 frame.usable = false; // To be decided after source path mapping.
2629 frame.function = QLatin1String(frameMi.findChild("func").data());
2630 frame.from = QLatin1String(frameMi.findChild("from").data());
2631 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2632 rc.push_back(frame);
2637 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2639 // Parse frames, find current. Special handling for step into:
2640 // When stepping into on an actual function (source mode) by executing 't', an assembler
2641 // frame pointing at the jmp instruction is hit (noticeable by top function being
2642 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2643 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2645 StackFrames frames = parseFrames(data);
2646 const int count = frames.size();
2647 for (int i = 0; i < count; i++) {
2648 const bool hasFile = !frames.at(i).file.isEmpty();
2649 // jmp-frame hit by step into, do another 't' and abort sequence.
2650 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2651 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2652 return ParseStackStepInto;
2655 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2656 frames[i].file = fileName.fileName;
2657 frames[i].usable = fileName.exists;
2658 if (current == -1 && frames[i].usable)
2662 if (count && current == -1) // No usable frame, use assembly.
2665 stackHandler()->setFrames(frames);
2666 activateFrame(current);
2670 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2672 if (command->success) {
2674 data.fromString(command->reply);
2675 parseStackTrace(data, false);
2676 postCommandSequence(command->commandSequence);
2678 showMessage(command->errorMessage, LogError);
2682 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2685 if (command->success) {
2686 value = command->reply.toInt();
2688 showMessage(command->errorMessage, LogError);
2690 // Is this a conditional breakpoint?
2691 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2692 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2693 const QString message = value ?
2694 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2695 arg(value).arg(cookie.id.toString()) :
2696 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2697 arg(cookie.id.toString());
2698 showMessage(message, LogMisc);
2699 // Stop if evaluation is true, else continue
2701 processStop(cookie.stopReason, true);
2703 postCommand("g", 0);
2708 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2710 if (exp.contains(' ') && !exp.startsWith('"')) {
2714 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2717 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2719 postCommandSequence(command->commandSequence);
2722 // Post a sequence of standard commands: Trigger next once one completes successfully
2723 void CdbEngine::postCommandSequence(unsigned mask)
2726 qDebug("postCommandSequence 0x%x\n", mask);
2730 if (mask & CommandListThreads) {
2731 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2734 if (mask & CommandListStack) {
2735 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2738 if (mask & CommandListRegisters) {
2739 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2740 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2743 if (mask & CommandListModules) {
2744 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2747 if (mask & CommandListBreakPoints) {
2748 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2749 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2754 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2756 bool success = false;
2759 if (!reply->success) {
2760 message = QString::fromAscii(reply->errorMessage);
2763 // Should be "namespace::QWidget:0x555"
2764 QString watchExp = QString::fromAscii(reply->reply);
2765 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2767 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2770 // 0x000 -> nothing found
2771 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2772 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2775 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2776 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2777 watchExp.insert(0, QLatin1String("*("));
2778 watchHandler()->watchExpression(watchExp);
2782 showMessage(message, LogWarning);
2783 m_watchPointX = m_watchPointY = 0;
2786 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2789 str << "Obtained breakpoint " << id << " (#" << r.id.majorPart() << ')';
2793 str.setIntegerBase(16);
2794 str << ", at 0x" << r.address;
2795 str.setIntegerBase(10);
2798 str << ", disabled";
2799 if (!r.module.isEmpty())
2800 str << ", module: '" << r.module << '\'';
2804 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2806 if (debugBreakpoints)
2807 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2808 if (!reply->success) {
2809 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2813 value.fromString(reply->reply);
2814 if (value.type() != GdbMi::List) {
2815 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2818 handleBreakPoints(value);
2821 void CdbEngine::handleBreakPoints(const GdbMi &value)
2823 // Report all obtained parameters back. Note that not all parameters are reported
2824 // back, so, match by id and complete
2825 if (debugBreakpoints)
2826 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2828 QTextStream str(&message);
2829 BreakHandler *handler = breakHandler();
2830 foreach (const GdbMi &breakPointG, value.children()) {
2831 BreakpointResponse reportedResponse;
2832 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2833 if (debugBreakpoints)
2834 qDebug(" Parsed %d: pending=%d %s\n", id.majorPart(),
2835 reportedResponse.pending,
2836 qPrintable(reportedResponse.toString()));
2838 if (!reportedResponse.pending) {
2839 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2840 if (it != m_pendingBreakpointMap.end()) {
2841 // Complete the response and set on handler.
2842 BreakpointResponse ¤tResponse = it.value();
2843 currentResponse.id = reportedResponse.id;
2844 currentResponse.address = reportedResponse.address;
2845 currentResponse.module = reportedResponse.module;
2846 currentResponse.pending = reportedResponse.pending;
2847 currentResponse.enabled = reportedResponse.enabled;
2848 formatCdbBreakPointResponse(id, currentResponse, str);
2849 if (debugBreakpoints)
2850 qDebug(" Setting for %d: %s\n", id.majorPart(),
2851 qPrintable(currentResponse.toString()));
2852 handler->setResponse(id, currentResponse);
2853 m_pendingBreakpointMap.erase(it);
2855 } // not pending reported
2857 if (m_pendingBreakpointMap.empty()) {
2858 str << QLatin1String("All breakpoints have been resolved.\n");
2860 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2862 showMessage(message, LogMisc);
2865 void CdbEngine::watchPoint(const QPoint &p)
2867 m_watchPointX = p.x();
2868 m_watchPointY = p.y();
2870 case InferiorStopOk:
2871 postWidgetAtCommand();
2874 // "Select Widget to Watch" from a running application is currently not
2875 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2876 // but requires some work as not to confuse the engine by state-change notifications
2877 // emitted by the debuggee function call.
2878 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2881 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2882 arg(QString::fromAscii(stateName(state()))), LogWarning);
2887 void CdbEngine::postWidgetAtCommand()
2889 QByteArray arguments = QByteArray::number(m_watchPointX);
2890 arguments.append(' ');
2891 arguments.append(QByteArray::number(m_watchPointY));
2892 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2895 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2897 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2898 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2899 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2902 if (qVariantCanConvert<MemoryViewCookie>(v)) {
2903 postFetchMemory(qVariantValue<MemoryViewCookie>(v));
2908 } // namespace Internal
2909 } // namespace Debugger