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(BreakpointModelId i = BreakpointModelId()) : id(i) {}
196 BreakpointModelId id;
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)
327 // Accessed by RunControlFactory
328 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
329 DebuggerEngine *masterEngine, QString *errorMessage)
332 CdbOptionsPage *op = CdbOptionsPage::instance();
333 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
334 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
337 return new CdbEngine(sp, masterEngine, op->options());
339 Q_UNUSED(masterEngine)
342 *errorMessage = QString::fromLatin1("Unsupported debug mode");
346 bool isCdbEngineEnabled()
349 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
355 static inline QString msgNoCdbBinaryForToolChain(const Abi &tc)
357 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
360 static inline bool isMsvcFlavor(Abi::OSFlavor osf)
362 return osf == Abi::WindowsMsvc2005Flavor
363 || osf == Abi::WindowsMsvc2008Flavor
364 || osf == Abi::WindowsMsvc2010Flavor;
367 static QString cdbBinary(const DebuggerStartParameters &sp)
369 if (!sp.debuggerCommand.isEmpty()) {
370 // Do not use a GDB binary if we got started for a project with MinGW runtime.
371 const bool abiMatch = sp.toolChainAbi.os() == Abi::WindowsOS
372 && isMsvcFlavor(sp.toolChainAbi.osFlavor());
374 return sp.debuggerCommand;
376 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
379 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
382 if (!isCdbEngineEnabled()) {
383 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
384 arg(sp.toolChainAbi.toString()));
385 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
386 check->settingsPage = CdbOptionsPage::settingsId();
390 if (!validMode(sp.startMode)) {
391 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
395 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
396 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
397 arg(sp.toolChainAbi.toString()));
401 if (sp.startMode == AttachCore && !isMsvcFlavor(sp.toolChainAbi.osFlavor())) {
402 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine cannot debug gdb core files."));
406 if (cdbBinary(sp).isEmpty()) {
407 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
408 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
409 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
416 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
421 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
424 opts->push_back(new CdbOptionsPage);
430 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
432 static inline Utils::SavedAction *theAssemblerAction()
434 return debuggerCore()->action(OperateByInstruction);
437 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
438 DebuggerEngine *masterEngine, const OptionsPtr &options) :
439 DebuggerEngine(sp, masterEngine),
440 m_creatorExtPrefix("<qtcreatorcdbext>|"),
441 m_tokenPrefix("<token>"),
443 m_effectiveStartMode(NoStartMode),
445 m_specialStopMode(NoSpecialStop),
446 m_nextCommandToken(0),
447 m_currentBuiltinCommandIndex(-1),
448 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
449 m_operateByInstructionPending(true),
450 m_operateByInstruction(true), // Default CDB setting
451 m_notifyEngineShutdownOnTermination(false),
452 m_hasDebuggee(false),
454 m_sourceStepInto(false),
457 m_ignoreCdbOutput(false)
459 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
461 setObjectName(QLatin1String("CdbEngine"));
462 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
463 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
464 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
465 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
468 void CdbEngine::init()
470 m_effectiveStartMode = NoStartMode;
471 notifyInferiorPid(0);
472 m_accessible = false;
473 m_specialStopMode = NoSpecialStop;
474 m_nextCommandToken = 0;
475 m_currentBuiltinCommandIndex = -1;
476 m_operateByInstructionPending = theAssemblerAction()->isChecked();
477 m_operateByInstruction = true; // Default CDB setting
478 m_notifyEngineShutdownOnTermination = false;
479 m_hasDebuggee = false;
480 m_sourceStepInto = false;
481 m_watchPointX = m_watchPointY = 0;
482 m_ignoreCdbOutput = false;
484 m_outputBuffer.clear();
485 m_builtinCommandQueue.clear();
486 m_extensionCommandQueue.clear();
487 m_extensionMessageBuffer.clear();
488 m_pendingBreakpointMap.clear();
489 m_customSpecialStopData.clear();
490 m_symbolAddressCache.clear();
491 m_coreStopReason.reset();
493 // Create local list of mappings in native separators
494 m_sourcePathMappings.clear();
495 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
496 if (!globalOptions->sourcePathMap.isEmpty()) {
497 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
498 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
499 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
500 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
501 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
502 QDir::toNativeSeparators(it.value())));
505 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
508 CdbEngine::~CdbEngine()
512 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
514 // To be set next time session becomes accessible
515 m_operateByInstructionPending = operateByInstruction;
516 if (state() == InferiorStopOk)
517 syncOperateByInstruction(operateByInstruction);
520 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
523 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
524 if (m_operateByInstruction == operateByInstruction)
526 QTC_ASSERT(m_accessible, return; )
527 m_operateByInstruction = operateByInstruction;
528 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
529 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
532 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
533 TextEditor::ITextEditor *editor,
534 const DebuggerToolTipContext &contextIn)
537 qDebug() << Q_FUNC_INFO;
538 // Need a stopped debuggee and a cpp file in a valid frame
539 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
541 // Determine expression and function
544 DebuggerToolTipContext context = contextIn;
545 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
546 // Are we in the current stack frame
547 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
549 // No numerical or any other expressions [yet]
550 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
552 // Can this be found as a local variable?
553 const QByteArray localsPrefix(localsPrefixC);
554 QByteArray iname = localsPrefix + exp.toAscii();
555 QModelIndex index = watchHandler()->itemIndex(iname);
556 if (!index.isValid()) {
557 // Nope, try a 'local.this.m_foo'.
558 exp.prepend(QLatin1String("this."));
559 iname.insert(localsPrefix.size(), "this.");
560 index = watchHandler()->itemIndex(iname);
561 if (!index.isValid())
564 DebuggerToolTipWidget *tw = new DebuggerToolTipWidget;
565 tw->setContext(context);
566 tw->setDebuggerModel(LocalsWatch);
567 tw->setExpression(exp);
568 tw->acquireEngine(this);
569 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
573 // Determine full path to the CDB extension library.
574 QString CdbEngine::extensionLibraryName(bool is64Bit)
576 // Determine extension lib name and path to use
578 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
579 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
580 << '/' << QT_CREATOR_CDB_EXT << ".dll";
584 // Determine environment for CDB.exe, start out with run config and
585 // add CDB extension path merged with system value should there be one.
586 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
587 QString cdbExtensionPath)
589 // Determine CDB extension path from Qt Creator
590 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
591 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
592 if (!oldCdbExtensionPath.isEmpty()) {
593 cdbExtensionPath.append(QLatin1Char(';'));
594 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
596 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
597 // config, just to make sure, delete any existing entries
598 const QString cdbExtensionPathVariableAssign =
599 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
600 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
601 if (it->startsWith(cdbExtensionPathVariableAssign)) {
602 it = runConfigEnvironment.erase(it);
608 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
609 QDir::toNativeSeparators(cdbExtensionPath));
610 return runConfigEnvironment;
613 int CdbEngine::elapsedLogTime() const
615 const int elapsed = m_logTime.elapsed();
616 const int delta = elapsed - m_elapsedLogTime;
617 m_elapsedLogTime = elapsed;
621 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
622 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
625 qDebug("startConsole %s", qPrintable(sp.executable));
626 m_consoleStub.reset(new Utils::ConsoleProcess);
627 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
628 connect(m_consoleStub.data(), SIGNAL(processError(QString)),
629 SLOT(consoleStubError(QString)));
630 connect(m_consoleStub.data(), SIGNAL(processStarted()),
631 SLOT(consoleStubProcessStarted()));
632 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
633 SLOT(consoleStubExited()));
634 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
635 if (sp.environment.size())
636 m_consoleStub->setEnvironment(sp.environment);
637 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
638 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
644 void CdbEngine::consoleStubError(const QString &msg)
647 qDebug("consoleStubProcessMessage() in %s %s", stateName(state()), qPrintable(msg));
648 if (state() == EngineSetupRequested) {
649 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
650 notifyEngineSetupFailed();
652 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
655 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
658 void CdbEngine::consoleStubProcessStarted()
661 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
662 // Attach to console process.
663 DebuggerStartParameters attachParameters = startParameters();
664 attachParameters.executable.clear();
665 attachParameters.processArgs.clear();
666 attachParameters.attachPID = m_consoleStub->applicationPID();
667 attachParameters.startMode = AttachExternal;
668 attachParameters.useTerminal = false;
669 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
670 QString errorMessage;
671 if (!launchCDB(attachParameters, &errorMessage)) {
672 showMessage(errorMessage, LogError);
673 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
674 notifyEngineSetupFailed();
678 void CdbEngine::consoleStubExited()
682 void CdbEngine::setupEngine()
685 qDebug(">setupEngine");
686 // Nag to add symbol server
687 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
688 &(m_options->symbolPaths)))
689 m_options->toSettings(Core::ICore::instance()->settings());
692 if (!m_logTime.elapsed())
694 QString errorMessage;
695 // Console: Launch the stub with the suspended application and attach to it
696 // CDB in theory has a command line option '-2' that launches a
697 // console, too, but that immediately closes when the debuggee quits.
698 // Use the Creator stub instead.
699 const DebuggerStartParameters &sp = startParameters();
700 const bool launchConsole = isCreatorConsole(sp, *m_options);
701 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
702 const bool ok = launchConsole ?
703 startConsole(startParameters(), &errorMessage) :
704 launchCDB(startParameters(), &errorMessage);
706 qDebug("<setupEngine ok=%d", ok);
708 showMessage(errorMessage, LogError);
709 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
710 notifyEngineSetupFailed();
714 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
717 qDebug("launchCDB startMode=%d", sp.startMode);
718 const QChar blank(QLatin1Char(' '));
719 // Start engine which will run until initial breakpoint:
720 // Determine binary (force MSVC), extension lib name and path to use
721 // The extension is passed as relative name with the path variable set
722 //(does not work with absolute path names)
723 const QString executable = cdbBinary(sp);
724 if (executable.isEmpty()) {
725 *errorMessage = tr("There is no CDB executable specified.");
731 Utils::winIs64BitBinary(executable);
735 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
736 if (!extensionFi.isFile()) {
737 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
738 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
741 const QString extensionFileName = extensionFi.fileName();
743 QStringList arguments;
744 const bool isRemote = sp.startMode == AttachToRemote;
745 if (isRemote) { // Must be first
746 arguments << QLatin1String("-remote") << sp.remoteChannel;
748 arguments << (QLatin1String("-a") + extensionFileName);
750 // Source line info/No terminal breakpoint / Pull extension
751 arguments << QLatin1String("-lines") << QLatin1String("-G")
752 // register idle (debuggee stop) notification
753 << QLatin1String("-c")
754 << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
755 if (sp.useTerminal) // Separate console
756 arguments << QLatin1String("-2");
757 if (!m_options->symbolPaths.isEmpty())
758 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
759 if (!m_options->sourcePaths.isEmpty())
760 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
761 // Compile argument string preserving quotes
762 QString nativeArguments = m_options->additionalArguments;
763 switch (sp.startMode) {
766 if (!nativeArguments.isEmpty())
767 nativeArguments.push_back(blank);
768 nativeArguments += QDir::toNativeSeparators(sp.executable);
773 case AttachCrashedExternal:
774 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
775 if (sp.startMode == AttachCrashedExternal) {
776 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
778 if (isCreatorConsole(startParameters(), *m_options))
779 arguments << QLatin1String("-pr") << QLatin1String("-pb");
783 arguments << QLatin1String("-z") << sp.coreFile;
786 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
789 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
790 if (!nativeArguments.isEmpty())
791 nativeArguments.push_back(blank);
792 nativeArguments += sp.processArgs;
795 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
796 arg(QDir::toNativeSeparators(executable),
797 arguments.join(QString(blank)) + blank + nativeArguments,
798 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
799 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
800 showMessage(msg, LogMisc);
802 m_outputBuffer.clear();
803 const QStringList environment = sp.environment.size() == 0 ?
804 QProcessEnvironment::systemEnvironment().toStringList() :
805 sp.environment.toStringList();
806 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
807 if (!sp.workingDirectory.isEmpty())
808 m_process.setWorkingDirectory(sp.workingDirectory);
811 if (!nativeArguments.isEmpty()) // Appends
812 m_process.setNativeArguments(nativeArguments);
814 m_process.start(executable, arguments);
815 if (!m_process.waitForStarted()) {
816 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
817 arg(QDir::toNativeSeparators(executable), m_process.errorString());
821 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
823 const unsigned long pid = 0;
825 showMessage(QString::fromLatin1("%1 running as %2").
826 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
827 m_hasDebuggee = true;
828 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
830 const QByteArray loadCommand = QByteArray(".load ")
831 + extensionFileName.toLocal8Bit();
832 postCommand(loadCommand, 0);
833 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
834 notifyEngineSetupOk();
839 void CdbEngine::setupInferior()
842 qDebug("setupInferior");
843 if (!isSlaveEngine()) {
844 // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
845 // (attemptBreakpointSynchronization() will be directly called then)
846 attemptBreakpointSynchronization();
847 if (startParameters().breakOnMain) {
848 const BreakpointParameters bp(BreakpointAtMain);
849 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings,
850 BreakpointModelId(-1), true), 0);
853 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
854 postCommand(".asm source_line", 0); // Source line in assembly
855 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
858 void CdbEngine::runEngine()
862 foreach (const QString &breakEvent, m_options->breakEvents)
863 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
864 if (startParameters().startMode == AttachCore) {
865 QTC_ASSERT(!m_coreStopReason.isNull(), return; );
866 notifyInferiorUnrunnable();
867 processStop(*m_coreStopReason, false);
873 bool CdbEngine::commandsPending() const
875 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
878 void CdbEngine::shutdownInferior()
881 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
882 isCdbProcessRunning());
884 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
886 qDebug("notifyInferiorShutdownOk");
887 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
888 notifyInferiorShutdownOk();
892 if (m_accessible) { // except console.
893 if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
895 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
896 notifyInferiorShutdownOk();
898 // A command got stuck.
899 if (commandsPending()) {
900 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
901 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
902 notifyInferiorShutdownFailed();
905 if (!canInterruptInferior()) {
906 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
907 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
908 notifyInferiorShutdownFailed();
911 interruptInferior(); // Calls us again
915 /* shutdownEngine/processFinished:
916 * Note that in the case of launching a process by the debugger, the debugger
917 * automatically quits a short time after reporting the session becoming
918 * inaccessible without debuggee (notifyInferiorExited). In that case,
919 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
920 * as not to confuse the state engine.
923 void CdbEngine::shutdownEngine()
926 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
927 "accessible=%d,commands pending=%d",
928 stateName(state()), isCdbProcessRunning(), m_accessible,
931 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
932 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
933 notifyEngineShutdownOk();
937 // No longer trigger anything from messages
938 m_ignoreCdbOutput = true;
939 // Go for kill if there are commands pending.
940 if (m_accessible && !commandsPending()) {
941 // detach (except console): Wait for debugger to finish.
942 if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
944 // Remote requires a bit more force to quit.
945 if (m_effectiveStartMode == AttachToRemote) {
946 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
947 postCommand("qq", 0);
951 m_notifyEngineShutdownOnTermination = true;
954 // Remote process. No can do, currently
955 m_notifyEngineShutdownOnTermination = true;
956 Utils::SynchronousProcess::stopProcess(m_process);
959 // Lost debuggee, debugger should quit anytime now
960 if (!m_hasDebuggee) {
961 m_notifyEngineShutdownOnTermination = true;
967 void CdbEngine::processFinished()
970 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
971 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
972 m_process.exitStatus(), m_process.exitCode());
974 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
976 showMessage(tr("CDB crashed"), LogError); // not in your life.
978 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
981 if (m_notifyEngineShutdownOnTermination) {
984 qDebug("notifyEngineIll");
985 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
988 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
989 notifyEngineShutdownOk();
992 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
993 // Otherwise, we take a shortcut.
994 if (isSlaveEngine()) {
995 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
996 notifyInferiorExited();
998 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
999 notifyEngineSpontaneousShutdown();
1004 void CdbEngine::detachDebugger()
1006 postCommand(".detach", 0);
1009 static inline bool isWatchIName(const QByteArray &iname)
1011 return iname.startsWith("watch");
1014 void CdbEngine::updateWatchData(const WatchData &dataIn,
1015 const WatchUpdateFlags & flags)
1017 if (debug || debugLocals || debugWatches)
1018 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
1019 elapsedLogTime(), m_accessible, stateName(state()),
1020 flags.tryIncremental,
1021 qPrintable(dataIn.toString()));
1023 if (!m_accessible) // Add watch data while running?
1027 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
1029 ByteArrayInputStream str(args);
1030 str << dataIn.iname << " \"" << dataIn.exp << '"';
1031 postExtensionCommand("addwatch", args, 0,
1032 &CdbEngine::handleAddWatch, 0,
1033 qVariantFromValue(dataIn));
1037 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
1038 WatchData data = dataIn;
1039 data.setAllUnneeded();
1040 watchHandler()->insertData(data);
1043 updateLocalVariable(dataIn.iname);
1046 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1048 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1050 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1051 if (reply->success) {
1052 updateLocalVariable(item.iname);
1054 item.setError(tr("Unable to add expression"));
1055 watchHandler()->insertData(item);
1056 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1057 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1058 reply->errorMessage), LogError);
1062 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1064 if (debuggerCore()->boolSetting(VerboseLog))
1065 str << blankSeparator << "-v";
1066 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1067 str << blankSeparator << "-c";
1068 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1069 if (!typeFormats.isEmpty())
1070 str << blankSeparator << "-T " << typeFormats;
1071 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1072 if (!individualFormats.isEmpty())
1073 str << blankSeparator << "-I " << individualFormats;
1076 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1078 const bool isWatch = isWatchIName(iname);
1080 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1081 QByteArray localsArguments;
1082 ByteArrayInputStream str(localsArguments);
1083 addLocalsOptions(str);
1085 const int stackFrame = stackHandler()->currentIndex();
1086 if (stackFrame < 0) {
1087 qWarning("Internal error; no stack frame in updateLocalVariable");
1090 str << blankSeparator << stackFrame;
1092 str << blankSeparator << iname;
1093 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1096 unsigned CdbEngine::debuggerCapabilities() const
1098 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1099 |WatchpointByAddressCapability|JumpToLineCapability|AddWatcherCapability|WatchWidgetsCapability
1100 |ReloadModuleCapability
1101 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1102 |BreakConditionCapability|TracePointCapability
1103 |BreakModuleCapability
1104 |OperateByInstructionCapability
1105 |RunToLineCapability;
1108 void CdbEngine::executeStep()
1110 if (!m_operateByInstruction)
1111 m_sourceStepInto = true; // See explanation at handleStackTrace().
1112 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1113 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1114 notifyInferiorRunRequested();
1117 void CdbEngine::executeStepOut()
1119 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1120 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1121 notifyInferiorRunRequested();
1124 void CdbEngine::executeNext()
1126 postCommand(QByteArray("p"), 0); // Step over -> p
1127 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1128 notifyInferiorRunRequested();
1131 void CdbEngine::executeStepI()
1136 void CdbEngine::executeNextI()
1141 void CdbEngine::continueInferior()
1143 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1144 notifyInferiorRunRequested();
1145 doContinueInferior();
1148 void CdbEngine::doContinueInferior()
1150 postCommand(QByteArray("g"), 0);
1153 bool CdbEngine::canInterruptInferior() const
1155 return m_effectiveStartMode != AttachToRemote && inferiorPid();
1158 void CdbEngine::interruptInferior()
1161 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1162 if (canInterruptInferior()) {
1163 doInterruptInferior(NoSpecialStop);
1165 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1166 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1167 notifyInferiorStopOk();
1168 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1169 notifyInferiorRunRequested();
1170 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1171 notifyInferiorRunOk();
1175 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1177 if (m_specialStopMode == NoSpecialStop)
1178 doInterruptInferior(CustomSpecialStop);
1179 m_customSpecialStopData.push_back(v);
1182 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1185 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1186 m_specialStopMode = sm;
1187 QString errorMessage;
1188 showMessage(QString::fromLatin1("Interrupting process %1...").arg(inferiorPid()), LogMisc);
1189 if (!winDebugBreakProcess(inferiorPid(), &errorMessage)) {
1190 m_specialStopMode = oldSpecialMode;
1191 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(inferiorPid()).arg(errorMessage), LogError);
1198 void CdbEngine::executeRunToLine(const ContextData &data)
1200 // Add one-shot breakpoint
1201 BreakpointParameters bp;
1203 bp.type =BreakpointByAddress;
1204 bp.address = data.address;
1206 bp.type =BreakpointByFileAndLine;
1207 bp.fileName = data.fileName;
1208 bp.lineNumber = data.lineNumber;
1210 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(-1), true), 0);
1214 void CdbEngine::executeRunToFunction(const QString &functionName)
1216 // Add one-shot breakpoint
1217 BreakpointParameters bp(BreakpointByFunction);
1218 bp.functionName = functionName;
1220 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(-1), true), 0);
1224 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1226 const Registers registers = registerHandler()->registers();
1227 QTC_ASSERT(regnr < registers.size(), return)
1228 // Value is decimal or 0x-hex-prefixed
1230 ByteArrayInputStream str(cmd);
1231 str << "r " << registers.at(regnr).name << '=' << value;
1232 postCommand(cmd, 0);
1236 void CdbEngine::executeJumpToLine(const ContextData &data)
1239 // Goto address directly.
1240 jumpToAddress(data.address);
1241 gotoLocation(Location(data.address));
1243 // Jump to source line: Resolve source line address and go to that location
1245 ByteArrayInputStream str(cmd);
1246 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1247 const QVariant cookie = qVariantFromValue(data);
1248 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1252 void CdbEngine::jumpToAddress(quint64 address)
1254 // Fake a jump to address by setting the PC register.
1255 QByteArray registerCmd;
1256 ByteArrayInputStream str(registerCmd);
1257 // PC-register depending on 64/32bit.
1258 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1259 str.setHexPrefix(true);
1260 str.setIntegerBase(16);
1262 postCommand(registerCmd, 0);
1265 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1267 if (cmd->reply.isEmpty())
1269 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1270 // Set register 'rip' to hex address and goto lcoation
1271 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1272 const int equalPos = answer.indexOf(" = ");
1275 answer.remove(0, equalPos + 3);
1276 answer.remove(QLatin1Char('`'));
1278 const quint64 address = answer.toLongLong(&ok, 16);
1279 if (ok && address) {
1280 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1281 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1282 jumpToAddress(address);
1283 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1287 static inline bool isAsciiWord(const QString &s)
1289 foreach (const QChar &c, s) {
1290 if (!c.isLetterOrNumber() || c.toAscii() == 0)
1296 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1299 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1301 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1302 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1306 ByteArrayInputStream str(cmd);
1307 switch (value.type()) {
1308 case QVariant::String: {
1309 // Convert qstring to Utf16 data not considering endianness for Windows.
1310 const QString s = value.toString();
1311 if (isAsciiWord(s)) {
1312 str << m_extensionCommandPrefixBA << "assign \"" << w->iname << '='
1313 << s.toLatin1() << '"';
1315 const QByteArray utf16(reinterpret_cast<const char *>(s.utf16()), 2 * s.size());
1316 str << m_extensionCommandPrefixBA << "assign -u " << w->iname << '='
1322 str << m_extensionCommandPrefixBA << "assign " << w->iname << '='
1323 << value.toString();
1327 postCommand(cmd, 0);
1328 // Update all locals in case we change a union or something pointed to
1329 // that affects other variables, too.
1333 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1335 int currentThreadId;
1336 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1337 threadsHandler()->setThreads(threads);
1338 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1339 forceCurrentThreadId : currentThreadId);
1342 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1345 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1346 if (reply->success) {
1348 data.fromString(reply->reply);
1350 // Continue sequence
1351 postCommandSequence(reply->commandSequence);
1353 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1357 void CdbEngine::executeDebuggerCommand(const QString &command)
1359 postCommand(command.toLocal8Bit(), QuietCommand);
1362 // Post command without callback
1363 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1366 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1367 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1368 if (!(flags & QuietCommand))
1369 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1370 m_process.write(cmd + '\n');
1373 // Post a built-in-command producing free-format output with a callback.
1374 // In order to catch the output, it is enclosed in 'echo' commands
1375 // printing a specially formatted token to be identifiable in the output.
1376 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1377 BuiltinCommandHandler handler,
1378 unsigned nextCommandFlag,
1379 const QVariant &cookie)
1381 if (!m_accessible) {
1382 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1383 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1384 showMessage(msg, LogError);
1387 if (!flags & QuietCommand)
1388 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1390 const int token = m_nextCommandToken++;
1391 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1393 m_builtinCommandQueue.push_back(pendingCommand);
1394 // Enclose command in echo-commands for token
1396 ByteArrayInputStream str(fullCmd);
1397 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1398 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1400 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1401 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1402 m_builtinCommandQueue.size(), nextCommandFlag);
1404 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1405 fullCmd.constData());
1406 m_process.write(fullCmd);
1409 // Post an extension command producing one-line output with a callback,
1410 // pass along token for identification in queue.
1411 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1412 const QByteArray &arguments,
1414 ExtensionCommandHandler handler,
1415 unsigned nextCommandFlag,
1416 const QVariant &cookie)
1418 if (!m_accessible) {
1419 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1420 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1421 showMessage(msg, LogError);
1425 const int token = m_nextCommandToken++;
1427 // Format full command with token to be recognizeable in the output
1429 ByteArrayInputStream str(fullCmd);
1430 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1431 if (!arguments.isEmpty())
1432 str << ' ' << arguments;
1434 if (!flags & QuietCommand)
1435 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1437 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1439 m_extensionCommandQueue.push_back(pendingCommand);
1440 // Enclose command in echo-commands for token
1442 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1443 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1444 m_extensionCommandQueue.size(), nextCommandFlag);
1445 m_process.write(fullCmd + '\n');
1448 void CdbEngine::activateFrame(int index)
1450 // TODO: assembler,etc
1453 const StackFrames &frames = stackHandler()->frames();
1454 QTC_ASSERT(index < frames.size(), return; )
1456 const StackFrame frame = frames.at(index);
1457 if (debug || debugLocals)
1458 qDebug("activateFrame idx=%d '%s' %d", index,
1459 qPrintable(frame.file), frame.line);
1460 stackHandler()->setCurrentIndex(index);
1461 const bool showAssembler = !frames.at(index).isUsable();
1462 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1463 watchHandler()->beginCycle();
1464 watchHandler()->endCycle();
1465 QAction *assemblerAction = theAssemblerAction();
1466 if (assemblerAction->isChecked()) {
1467 gotoLocation(frame);
1469 assemblerAction->trigger(); // Seems to trigger update
1472 gotoLocation(frame);
1477 void CdbEngine::updateLocals(bool forNewStackFrame)
1479 typedef QHash<QByteArray, int> WatcherHash;
1481 const int frameIndex = stackHandler()->currentIndex();
1482 if (frameIndex < 0) {
1483 watchHandler()->beginCycle();
1484 watchHandler()->endCycle();
1487 const StackFrame frame = stackHandler()->currentFrame();
1488 if (!frame.isUsable()) {
1489 watchHandler()->beginCycle();
1490 watchHandler()->endCycle();
1493 /* Watchers: Forcibly discard old symbol group as switching from
1494 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1495 * and cause errors as it seems to go 'stale' when switching threads.
1496 * Initial expand, get uninitialized and query */
1497 QByteArray arguments;
1498 ByteArrayInputStream str(arguments);
1501 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1502 if (!expanded.isEmpty()) {
1503 str << blankSeparator << "-e ";
1505 foreach(const QByteArray &e, expanded) {
1511 addLocalsOptions(str);
1512 // Uninitialized variables if desired. Quote as safeguard against shadowed
1513 // variables in case of errors in uninitializedVariables().
1514 if (debuggerCore()->boolSetting(UseCodeModel)) {
1515 QStringList uninitializedVariables;
1516 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1517 frame.function, frame.file, frame.line, &uninitializedVariables);
1518 if (!uninitializedVariables.isEmpty()) {
1519 str << blankSeparator << "-u \"";
1521 foreach(const QString &u, uninitializedVariables) {
1524 str << localsPrefixC << u;
1529 // Perform watches synchronization
1530 str << blankSeparator << "-W";
1531 const WatcherHash watcherHash = WatchHandler::watcherNames();
1532 if (!watcherHash.isEmpty()) {
1533 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1534 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1535 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1539 // Required arguments: frame
1540 str << blankSeparator << frameIndex;
1541 watchHandler()->beginCycle();
1542 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1545 void CdbEngine::selectThread(int index)
1547 if (index < 0 || index == threadsHandler()->currentThread())
1551 const int newThreadId = threadsHandler()->threads().at(index).id;
1552 threadsHandler()->setCurrentThread(index);
1554 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1555 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1558 // Default address range for showing disassembly.
1559 enum { DisassemblerRange = 512 };
1561 /* Try to emulate gdb's behaviour: When passed an address, display
1562 * the disassembled function. CDB's 'u' (disassemble) command takes a symbol,
1563 * but does not display the whole function, only 10 lines per default.
1564 * So, to ensure the agent's
1565 * address is in that range, resolve the function symbol, cache it and
1566 * request the disassembly for a range that contains the agent's address. */
1568 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1570 QTC_ASSERT(m_accessible, return;)
1571 const QString function = agent->location().functionName();
1572 const QString module = agent->location().from();
1573 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1574 if (function.isEmpty() || module.isEmpty()) {
1575 // No function, display a default range.
1576 postDisassemblerCommand(agent->address(), cookie);
1578 postResolveSymbol(module, function, cookie);
1582 void CdbEngine::postDisassemblerCommand(quint64 address, const QVariant &cookie)
1584 postDisassemblerCommand(address - DisassemblerRange / 2,
1585 address + DisassemblerRange / 2, cookie);
1588 void CdbEngine::postDisassemblerCommand(quint64 address, quint64 endAddress,
1589 const QVariant &cookie)
1592 ByteArrayInputStream str(cmd);
1593 str << "u " << hex <<hexPrefixOn << address << ' ' << endAddress;
1594 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1597 void CdbEngine::postResolveSymbol(const QString &module, const QString &function,
1598 const QVariant &cookie)
1600 const QString symbol = module + QLatin1Char('!') + function;
1601 const QList<quint64> addresses = m_symbolAddressCache.values(symbol);
1602 if (addresses.isEmpty()) {
1603 QVariantList cookieList;
1604 cookieList << QVariant(symbol) << cookie;
1605 showMessage(QLatin1String("Resolving symbol: ") + symbol, LogMisc);
1606 postBuiltinCommand(QByteArray("x ") + symbol.toLatin1(), 0,
1607 &CdbEngine::handleResolveSymbol, 0,
1608 QVariant(cookieList));
1610 showMessage(QString::fromLatin1("Using cached addresses for %1.").
1611 arg(symbol), LogMisc);
1612 handleResolveSymbol(addresses, cookie);
1616 // Parse address from 'x' response.
1617 // "00000001`3f7ebe80 module!foo (void)"
1618 static inline quint64 resolvedAddress(const QByteArray &line)
1620 const int blankPos = line.indexOf(' ');
1621 if (blankPos >= 0) {
1622 QByteArray addressBA = line.left(blankPos);
1623 if (addressBA.size() > 9 && addressBA.at(8) == '`')
1624 addressBA.remove(8, 1);
1626 const quint64 address = addressBA.toULongLong(&ok, 16);
1633 void CdbEngine::handleResolveSymbol(const CdbBuiltinCommandPtr &command)
1635 QTC_ASSERT(command->cookie.type() == QVariant::List, return; );
1636 const QVariantList cookieList = command->cookie.toList();
1637 const QString symbol = cookieList.front().toString();
1638 // Insert all matches of (potentially) ambiguous symbols
1639 if (const int size = command->reply.size()) {
1640 for (int i = 0; i < size; i++) {
1641 if (const quint64 address = resolvedAddress(command->reply.at(i))) {
1642 m_symbolAddressCache.insert(symbol, address);
1643 showMessage(QString::fromLatin1("Obtained 0x%1 for %2 (#%3)").
1644 arg(address, 0, 16).arg(symbol).arg(i + 1), LogMisc);
1648 showMessage(QLatin1String("Symbol resolution failed: ")
1649 + QString::fromLatin1(command->joinedReply()),
1652 handleResolveSymbol(m_symbolAddressCache.values(symbol), cookieList.back());
1655 // Find the function address matching needle in a list of function
1656 // addresses obtained from the 'x' command. Check for the
1657 // mimimum POSITIVE offset (needle >= function address.)
1658 static inline quint64 findClosestFunctionAddress(const QList<quint64> &addresses,
1661 const int size = addresses.size();
1665 return addresses.front();
1666 int closestIndex = 0;
1667 quint64 closestOffset = 0xFFFFFFFF;
1668 for (int i = 0; i < size; i++) {
1669 if (addresses.at(i) <= needle) {
1670 const quint64 offset = needle - addresses.at(i);
1671 if (offset < closestOffset) {
1672 closestOffset = offset;
1677 return addresses.at(closestIndex);
1680 void CdbEngine::handleResolveSymbol(const QList<quint64> &addresses, const QVariant &cookie)
1682 // Disassembly mode: Determine suitable range containing the
1683 // agent's address within the function to display.
1684 if (qVariantCanConvert<DisassemblerAgent*>(cookie)) {
1685 DisassemblerAgent *agent = cookie.value<DisassemblerAgent *>();
1686 const quint64 agentAddress = agent->address();
1687 const quint64 functionAddress
1688 = findClosestFunctionAddress(addresses, agentAddress);
1689 if (functionAddress > 0 && functionAddress <= agentAddress) {
1690 quint64 endAddress = agentAddress + DisassemblerRange / 2;
1691 if (const quint64 remainder = endAddress % 8)
1692 endAddress += 8 - remainder;
1693 postDisassemblerCommand(functionAddress, endAddress, cookie);
1695 postDisassemblerCommand(agentAddress, cookie);
1701 // Parse: "00000000`77606060 cc int 3"
1702 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1704 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1705 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1706 agent->setContents(parseCdbDisassembler(command->reply));
1709 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1712 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1713 const MemoryViewCookie cookie(agent, editor, addr, length);
1715 postFetchMemory(cookie);
1717 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1721 void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
1724 ByteArrayInputStream str(args);
1725 str << cookie.address << ' ' << cookie.length;
1726 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0,
1727 qVariantFromValue(cookie));
1730 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1732 QTC_ASSERT(!data.isEmpty(), return; )
1733 if (!m_accessible) {
1734 const MemoryChangeCookie cookie(addr, data);
1735 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1737 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1741 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1743 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1744 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1745 if (command->success) {
1746 const QByteArray data = QByteArray::fromBase64(command->reply);
1747 if (unsigned(data.size()) == memViewCookie.length)
1748 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1749 memViewCookie.address, data);
1751 showMessage(QString::fromLocal8Bit(command->errorMessage), LogWarning);
1755 void CdbEngine::reloadModules()
1757 postCommandSequence(CommandListModules);
1760 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1764 void CdbEngine::loadAllSymbols()
1768 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1770 Q_UNUSED(moduleName)
1773 void CdbEngine::reloadRegisters()
1775 postCommandSequence(CommandListRegisters);
1778 void CdbEngine::reloadSourceFiles()
1782 void CdbEngine::reloadFullStack()
1785 qDebug("%s", Q_FUNC_INFO);
1786 postCommandSequence(CommandListStack);
1789 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1791 // Fails for core dumps.
1793 notifyInferiorPid(reply->reply.toULongLong());
1794 if (reply->success || startParameters().startMode == AttachCore) {
1795 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1796 notifyInferiorSetupOk();
1798 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1799 arg(QLatin1String(reply->errorMessage)), LogError);
1800 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1801 notifyInferiorSetupFailed();
1805 // Parse CDB gdbmi register syntax.
1806 static Register parseRegister(const GdbMi &gdbmiReg)
1809 reg.name = gdbmiReg.findChild("name").data();
1810 const GdbMi description = gdbmiReg.findChild("description");
1811 if (description.type() != GdbMi::Invalid) {
1813 reg.name += description.data();
1816 reg.value = gdbmiReg.findChild("value").data();
1820 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1822 if (reply->success) {
1824 value.fromString(reply->reply);
1825 if (value.type() == GdbMi::List) {
1827 modules.reserve(value.childCount());
1828 foreach (const GdbMi &gdbmiModule, value.children()) {
1830 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1831 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1832 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1833 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1834 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1835 module.symbolsRead = Module::ReadOk;
1836 modules.push_back(module);
1838 modulesHandler()->setModules(modules);
1840 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1841 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1844 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1845 arg(QLatin1String(reply->errorMessage)), LogError);
1847 postCommandSequence(reply->commandSequence);
1851 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1853 if (reply->success) {
1855 value.fromString(reply->reply);
1856 if (value.type() == GdbMi::List) {
1857 Registers registers;
1858 registers.reserve(value.childCount());
1859 foreach (const GdbMi &gdbmiReg, value.children())
1860 registers.push_back(parseRegister(gdbmiReg));
1861 registerHandler()->setAndMarkRegisters(registers);
1863 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1864 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1867 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1868 arg(QLatin1String(reply->errorMessage)), LogError);
1870 postCommandSequence(reply->commandSequence);
1873 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1875 if (reply->success) {
1876 QList<WatchData> watchData;
1878 root.fromString(reply->reply);
1879 QTC_ASSERT(root.isList(), return ; )
1881 qDebug() << root.toString(true, 4);
1883 // Courtesy of GDB engine
1884 foreach (const GdbMi &child, root.children()) {
1886 dummy.iname = child.findChild("iname").data();
1887 dummy.name = QLatin1String(child.findChild("name").data());
1888 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1890 watchHandler()->insertBulkData(watchData);
1891 watchHandler()->endCycle();
1893 QDebug nsp = qDebug().nospace();
1894 nsp << "Obtained " << watchData.size() << " items:\n";
1895 foreach (const WatchData &wd, watchData)
1896 nsp << wd.toString() <<'\n';
1898 const bool forNewStackFrame = reply->cookie.toBool();
1899 if (forNewStackFrame)
1900 emit stackFrameCompleted();
1902 showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1906 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1908 if (!reply->success)
1909 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1912 enum CdbExecutionStatus {
1913 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1914 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1915 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1916 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1917 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1918 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1919 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1921 static const char *cdbStatusName(unsigned long s)
1924 case CDB_STATUS_NO_CHANGE:
1928 case CDB_STATUS_GO_HANDLED:
1929 return "go_handled";
1930 case CDB_STATUS_GO_NOT_HANDLED:
1931 return "go_not_handled";
1932 case CDB_STATUS_STEP_OVER:
1934 case CDB_STATUS_STEP_INTO:
1936 case CDB_STATUS_BREAK:
1938 case CDB_STATUS_NO_DEBUGGEE:
1939 return "no_debuggee";
1940 case CDB_STATUS_STEP_BRANCH:
1941 return "step_branch";
1942 case CDB_STATUS_IGNORE_EVENT:
1943 return "ignore_event";
1944 case CDB_STATUS_RESTART_REQUESTED:
1945 return "restart_requested";
1946 case CDB_STATUS_REVERSE_GO:
1947 return "reverse_go";
1948 case CDB_STATUS_REVERSE_STEP_BRANCH:
1949 return "reverse_step_branch";
1950 case CDB_STATUS_REVERSE_STEP_OVER:
1951 return "reverse_step_over";
1952 case CDB_STATUS_REVERSE_STEP_INTO:
1953 return "reverse_step_into";
1958 /* Examine how to react to a stop. */
1959 enum StopActionFlags
1962 StopReportLog = 0x1,
1963 StopReportStatusMessage = 0x2,
1964 StopReportParseError = 0x4,
1965 StopShowExceptionMessageBox = 0x8,
1966 // Notify stop or just continue
1967 StopNotifyStop = 0x10,
1968 StopIgnoreContinue = 0x20,
1969 // Hit on break in artificial stop thread (created by DebugBreak()).
1970 StopInArtificialThread = 0x40,
1971 StopShutdownInProgress = 0x80 // Shutdown in progress
1974 static inline QString msgTracePointTriggered(BreakpointModelId id, const int number,
1975 const QString &threadId)
1977 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1978 .arg(id.toString()).arg(number).arg(threadId);
1981 static inline QString msgCheckingConditionalBreakPoint(BreakpointModelId id, const int number,
1982 const QByteArray &condition,
1983 const QString &threadId)
1985 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1986 .arg(id.toString()).arg(number).arg(threadId, QString::fromAscii(condition));
1989 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1991 QString *exceptionBoxMessage,
1992 bool conditionalBreakPointTriggered)
1994 // Report stop reason (GDBMI)
1996 if (targetState() == DebuggerFinished)
1997 rc |= StopShutdownInProgress;
1999 qDebug("%s", stopReason.toString(true, 4).constData());
2000 const QByteArray reason = stopReason.findChild("reason").data();
2001 if (reason.isEmpty()) {
2002 *message = tr("Malformed stop response received.");
2003 rc |= StopReportParseError|StopNotifyStop;
2006 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
2007 if (state() == InferiorStopOk) {
2008 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
2009 arg(QString::fromAscii(reason));
2010 rc |= StopReportLog;
2013 const int threadId = stopReason.findChild("threadId").data().toInt();
2014 if (reason == "breakpoint") {
2015 // Note: Internal breakpoints (run to line) are reported with id=0.
2016 // Step out creates temporary breakpoints with id 10000.
2017 BreakpointModelId id;
2019 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
2020 if (breakpointIdG.isValid()) {
2021 id = BreakpointModelId(breakpointIdG.data().toInt());
2022 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
2023 const BreakpointResponse parameters = breakHandler()->response(id);
2024 if (!parameters.message.isEmpty()) {
2025 showMessage(parameters.message + QLatin1Char('\n'), AppOutput);
2026 showMessage(parameters.message, LogMisc);
2028 // Trace point? Just report.
2029 number = parameters.id.majorPart();
2030 if (parameters.tracepoint) {
2031 *message = msgTracePointTriggered(id, number, QString::number(threadId));
2032 return StopReportLog|StopIgnoreContinue;
2034 // Trigger evaluation of BP expression unless we are already in the response.
2035 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
2036 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
2037 QString::number(threadId));
2038 ConditionalBreakPointCookie cookie(id);
2039 cookie.stopReason = stopReason;
2040 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
2041 return StopReportLog;
2044 id = BreakpointModelId();
2047 QString tid = QString::number(threadId);
2048 if (id && breakHandler()->type(id) == WatchpointAtAddress) {
2049 *message = msgWatchpointByAddressTriggered(id, number, breakHandler()->address(id), tid);
2050 } else if (id && breakHandler()->type(id) == WatchpointAtExpression) {
2051 *message = msgWatchpointByExpressionTriggered(id, number, breakHandler()->expression(id), tid);
2053 *message = msgBreakpointTriggered(id, number, tid);
2055 rc |= StopReportStatusMessage|StopNotifyStop;
2058 if (reason == "exception") {
2059 WinException exception;
2060 exception.fromGdbMI(stopReason);
2061 QString description = exception.toString();
2063 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
2064 // pulls DLLs. Avoid showing a 'stopped' Message box.
2065 if (exception.exceptionCode == winExceptionStartupCompleteTrap
2066 || exception.exceptionCode == winExceptionWX86Breakpoint)
2067 return StopNotifyStop;
2068 if (exception.exceptionCode == winExceptionCtrlPressed) {
2069 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
2070 *message = msgInterrupted();
2071 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
2074 if (isDebuggerWinException(exception.exceptionCode)) {
2075 rc |= StopReportStatusMessage|StopNotifyStop;
2076 // Detect interruption by DebugBreak() and force a switch to thread 0.
2077 if (exception.function == "ntdll!DbgBreakPoint")
2078 rc |= StopInArtificialThread;
2079 *message = msgInterrupted();
2083 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
2084 *message = description;
2085 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
2088 *message = msgStopped(QLatin1String(reason));
2089 rc |= StopReportStatusMessage|StopNotifyStop;
2093 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
2099 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
2100 elapsedLogTime(), messageBA.constData(),
2101 stateName(state()), m_specialStopMode);
2103 // Switch source level debugging
2104 syncOperateByInstruction(m_operateByInstructionPending);
2106 // Engine-special stop reasons: Breakpoints and setup
2107 const SpecialStopMode specialStopMode = m_specialStopMode;
2109 m_specialStopMode = NoSpecialStop;
2111 switch(specialStopMode) {
2112 case SpecialStopSynchronizeBreakpoints:
2114 qDebug("attemptBreakpointSynchronization in special stop");
2115 attemptBreakpointSynchronization();
2116 doContinueInferior();
2118 case SpecialStopGetWidgetAt:
2119 postWidgetAtCommand();
2121 case CustomSpecialStop:
2122 foreach (const QVariant &data, m_customSpecialStopData)
2123 handleCustomSpecialStop(data);
2124 m_customSpecialStopData.clear();
2125 doContinueInferior();
2131 if (state() == EngineSetupRequested) { // Temporary stop at beginning
2132 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
2133 notifyEngineSetupOk();
2134 // Store stop reason to be handled in runEngine().
2135 if (startParameters().startMode == AttachCore) {
2136 m_coreStopReason.reset(new GdbMi);
2137 m_coreStopReason->fromString(messageBA);
2143 stopReason.fromString(messageBA);
2144 processStop(stopReason, false);
2147 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
2149 // Further examine stop and report to user
2151 QString exceptionBoxMessage;
2152 int forcedThreadId = -1;
2153 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
2154 conditionalBreakPointTriggered);
2155 // Do the non-blocking log reporting
2156 if (stopFlags & StopReportLog)
2157 showMessage(message, LogMisc);
2158 if (stopFlags & StopReportStatusMessage)
2159 showStatusMessage(message);
2160 if (stopFlags & StopReportParseError)
2161 showMessage(message, LogError);
2162 // Ignore things like WOW64, report tracepoints.
2163 if (stopFlags & StopIgnoreContinue) {
2164 postCommand("g", 0);
2167 // Notify about state and send off command sequence to get stack, etc.
2168 if (stopFlags & StopNotifyStop) {
2169 if (startParameters().startMode != AttachCore) {
2170 if (state() == InferiorStopRequested) {
2171 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
2172 notifyInferiorStopOk();
2174 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
2175 notifyInferiorSpontaneousStop();
2178 // Prevent further commands from being sent if shutdown is in progress
2179 if (stopFlags & StopShutdownInProgress) {
2180 showMessage(QString::fromLatin1("Shutdown request detected..."));
2183 const bool sourceStepInto = m_sourceStepInto;
2184 m_sourceStepInto = false;
2185 // Start sequence to get all relevant data.
2186 if (stopFlags & StopInArtificialThread) {
2187 showMessage(tr("Switching to main thread..."), LogMisc);
2188 postCommand("~0 s", 0);
2190 // Re-fetch stack again.
2191 postCommandSequence(CommandListStack);
2193 const GdbMi stack = stopReason.findChild("stack");
2194 if (stack.isValid()) {
2195 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
2196 executeStep(); // Hit on a frame while step into, see parseStackTrace().
2200 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
2203 const GdbMi threads = stopReason.findChild("threads");
2204 if (threads.isValid()) {
2205 parseThreads(threads, forcedThreadId);
2207 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
2209 // Fire off remaining commands asynchronously
2210 if (!m_pendingBreakpointMap.isEmpty())
2211 postCommandSequence(CommandListBreakPoints);
2212 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
2213 postCommandSequence(CommandListRegisters);
2214 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2215 postCommandSequence(CommandListModules);
2217 // After the sequence has been sent off and CDB is pondering the commands,
2218 // pop up a message box for exceptions.
2219 if (stopFlags & StopShowExceptionMessageBox)
2220 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2223 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2225 const DebuggerState s = state();
2226 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2230 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2231 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2234 case EngineShutdownRequested:
2237 case InferiorShutdownRequested:
2245 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2247 const DebuggerState s = state();
2250 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2254 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2255 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2258 case EngineSetupRequested:
2260 case EngineRunRequested:
2261 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2262 notifyEngineRunAndInferiorRunOk();
2265 case InferiorStopOk:
2266 // Inaccessible without debuggee (exit breakpoint)
2267 // We go for spontaneous engine shutdown instead.
2268 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2270 qDebug("Lost debuggeee");
2271 m_hasDebuggee = false;
2274 case InferiorRunRequested:
2275 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2276 notifyInferiorRunOk();
2279 case EngineShutdownRequested:
2286 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2289 QDebug nospace = qDebug().nospace();
2290 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2291 << ' ' << stateName(state());
2292 if (t == 'N' || debug > 1) {
2293 nospace << ' ' << message;
2295 nospace << ' ' << message.size() << " bytes";
2299 // Is there a reply expected, some command queued?
2300 if (t == 'R' || t == 'N') {
2301 if (token == -1) { // Default token, user typed in extension command
2302 showMessage(QString::fromLatin1(message), LogMisc);
2305 const int index = indexOfCommand(m_extensionCommandQueue, token);
2307 // Did the command finish? Take off queue and complete, invoke CB
2308 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2310 command->success = true;
2311 command->reply = message;
2313 command->success = false;
2314 command->errorMessage = message;
2317 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2318 command->command.constData(), command->token, m_extensionCommandQueue.size());
2319 if (command->handler)
2320 (this->*(command->handler))(command);
2325 if (what == "debuggee_output") {
2326 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2330 if (what == "event") {
2331 showStatusMessage(QString::fromAscii(message), 5000);
2335 if (what == "session_accessible") {
2336 if (!m_accessible) {
2337 m_accessible = true;
2338 handleSessionAccessible(message.toULong());
2343 if (what == "session_inaccessible") {
2345 m_accessible = false;
2346 handleSessionInaccessible(message.toULong());
2351 if (what == "session_idle") {
2352 handleSessionIdle(message);
2356 if (what == "exception") {
2357 WinException exception;
2359 gdbmi.fromString(message);
2360 exception.fromGdbMI(gdbmi);
2361 const QString message = exception.toString(true);
2362 showStatusMessage(message);
2363 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2364 if (exception.exceptionCode == winExceptionCppException)
2365 showMessage(message + QLatin1Char('\n'), AppOutput);
2373 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2374 enum { CdbPromptLength = 7 };
2376 static inline bool isCdbPrompt(const QByteArray &c)
2378 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2379 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2380 && std::isdigit(c.at(4));
2383 // Check for '<token>32>' or '<token>32<'
2384 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2385 int *token, bool *isStart)
2389 const int tokenPrefixSize = tokenPrefix.size();
2390 const int size = c.size();
2391 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2393 switch (c.at(size - 1)) {
2403 if (!c.startsWith(tokenPrefix))
2406 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2410 void CdbEngine::parseOutputLine(QByteArray line)
2412 // The hooked output callback in the extension suppresses prompts,
2413 // it should happen only in initial and exit stages. Note however that
2414 // if the output is not hooked, sequences of prompts are possible which
2415 // can mix things up.
2416 while (isCdbPrompt(line))
2417 line.remove(0, CdbPromptLength);
2418 // An extension notification (potentially consisting of several chunks)
2419 if (line.startsWith(m_creatorExtPrefix)) {
2420 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2421 const char type = line.at(m_creatorExtPrefix.size());
2423 const int tokenPos = m_creatorExtPrefix.size() + 2;
2424 const int tokenEndPos = line.indexOf('|', tokenPos);
2425 QTC_ASSERT(tokenEndPos != -1, return)
2426 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2428 const int remainingChunksPos = tokenEndPos + 1;
2429 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2430 QTC_ASSERT(remainingChunksEndPos != -1, return)
2431 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2432 // const char 'serviceName'
2433 const int whatPos = remainingChunksEndPos + 1;
2434 const int whatEndPos = line.indexOf('|', whatPos);
2435 QTC_ASSERT(whatEndPos != -1, return)
2436 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2437 // Build up buffer, call handler once last chunk was encountered
2438 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2439 if (remainingChunks == 0) {
2440 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2441 m_extensionMessageBuffer.clear();
2445 // Check for command start/end tokens within which the builtin command
2446 // output is enclosed
2448 bool isStartToken = false;
2449 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2451 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2452 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2454 // If there is a current command, wait for end of output indicated by token,
2455 // command, trigger handler and finish, else append to its output.
2456 if (m_currentBuiltinCommandIndex != -1) {
2457 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2458 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2459 if (isCommandToken) {
2460 // Did the command finish? Invoke callback and remove from queue.
2462 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2463 currentCommand->command.constData(), currentCommand->token,
2464 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2465 QTC_ASSERT(token == currentCommand->token, return; );
2466 if (currentCommand->handler)
2467 (this->*(currentCommand->handler))(currentCommand);
2468 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2469 m_currentBuiltinCommandIndex = -1;
2471 // Record output of current command
2472 currentCommand->reply.push_back(line);
2475 } // m_currentCommandIndex
2476 if (isCommandToken) {
2477 // Beginning command token encountered, start to record output.
2478 const int index = indexOfCommand(m_builtinCommandQueue, token);
2479 QTC_ASSERT(isStartToken && index != -1, return; );
2480 m_currentBuiltinCommandIndex = index;
2481 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2483 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2487 showMessage(QString::fromLocal8Bit(line), LogMisc);
2490 void CdbEngine::readyReadStandardOut()
2492 if (m_ignoreCdbOutput)
2494 m_outputBuffer += m_process.readAllStandardOutput();
2495 // Split into lines and parse line by line.
2497 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2498 if (endOfLinePos == -1) {
2502 QByteArray line = m_outputBuffer.left(endOfLinePos);
2503 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2504 line.truncate(line.size() - 1);
2505 parseOutputLine(line);
2506 m_outputBuffer.remove(0, endOfLinePos + 1);
2511 void CdbEngine::readyReadStandardError()
2513 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2516 void CdbEngine::processError()
2518 showMessage(m_process.errorString(), LogError);
2522 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2523 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2525 QByteArray cmd(cmdC);
2526 ByteArrayInputStream str(cmd);
2527 foreach(const BreakpointData *bp, bps)
2528 str << ' ' << bp->bpNumber;
2533 bool CdbEngine::stateAcceptsBreakpointChanges() const
2537 case InferiorStopOk:
2545 bool CdbEngine::acceptsBreakpoint(BreakpointModelId id) const
2547 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2548 if (!DebuggerEngine::isCppBreakpoint(data))
2550 switch (data.type) {
2552 case BreakpointAtFork:
2553 case WatchpointAtExpression:
2554 case BreakpointAtSysCall:
2556 case WatchpointAtAddress:
2557 case BreakpointByFileAndLine:
2558 case BreakpointByFunction:
2559 case BreakpointByAddress:
2560 case BreakpointAtThrow:
2561 case BreakpointAtCatch:
2562 case BreakpointAtMain:
2563 case BreakpointAtExec:
2569 // Context for fixing file/line-type breakpoints, for delayed creation.
2570 class BreakpointCorrectionContext
2573 explicit BreakpointCorrectionContext(const CPlusPlus::Snapshot &s,
2574 const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy) :
2575 m_snapshot(s), m_workingCopy(workingCopy) {}
2577 unsigned fixLineNumber(const QString &fileName, unsigned lineNumber) const;
2580 const CPlusPlus::Snapshot m_snapshot;
2581 CPlusPlus::CppModelManagerInterface::WorkingCopy m_workingCopy;
2584 static CPlusPlus::Document::Ptr getParsedDocument(const QString &fileName,
2585 const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy,
2586 const CPlusPlus::Snapshot &snapshot)
2589 if (workingCopy.contains(fileName)) {
2590 src = workingCopy.source(fileName);
2592 Utils::FileReader reader;
2593 if (reader.fetch(fileName)) // ### FIXME error reporting
2594 src = QString::fromLocal8Bit(reader.data()); // ### FIXME encoding
2597 QByteArray source = snapshot.preprocessedCode(src, fileName);
2599 CPlusPlus::Document::Ptr doc = snapshot.documentFromSource(source, fileName);
2604 unsigned BreakpointCorrectionContext::fixLineNumber(const QString &fileName,
2605 unsigned lineNumber) const
2607 CPlusPlus::Document::Ptr doc = m_snapshot.document(fileName);
2608 if (!doc || !doc->translationUnit()->ast())
2609 doc = getParsedDocument(fileName, m_workingCopy, m_snapshot);
2611 CPlusPlus::FindCdbBreakpoint findVisitor(doc->translationUnit());
2612 const unsigned correctedLine = findVisitor(lineNumber);
2613 if (!correctedLine) {
2614 qWarning("Unable to find breakpoint location for %s:%d",
2615 qPrintable(QDir::toNativeSeparators(fileName)), lineNumber);
2619 qDebug("Code model: Breakpoint line %u -> %u in %s",
2620 lineNumber, correctedLine, qPrintable(fileName));
2621 return correctedLine;
2624 void CdbEngine::attemptBreakpointSynchronization()
2629 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2630 // Check if there is anything to be done at all.
2631 BreakHandler *handler = breakHandler();
2632 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2633 foreach (BreakpointModelId id, handler->unclaimedBreakpointIds())
2634 if (acceptsBreakpoint(id))
2635 handler->setEngine(id, this);
2637 // Quick check: is there a need to change something? - Populate module cache
2638 bool changed = false;
2639 const BreakpointModelIds ids = handler->engineBreakpointIds(this);
2640 foreach (BreakpointModelId id, ids) {
2641 switch (handler->state(id)) {
2642 case BreakpointInsertRequested:
2643 case BreakpointRemoveRequested:
2644 case BreakpointChangeRequested:
2647 case BreakpointInserted: {
2648 // Collect the new modules matching the files.
2649 // In the future, that information should be obtained from the build system.
2650 const BreakpointParameters &data = handler->breakpointData(id);
2651 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2652 m_fileNameModuleHash.insert(data.fileName, data.module);
2660 if (debugBreakpoints)
2661 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2662 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2666 if (!m_accessible) {
2668 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2669 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2672 // Add/Change breakpoints and store pending ones in map, since
2673 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2674 // handleBreakPoints will the complete that information and set it on the break handler.
2675 bool addedChanged = false;
2676 QScopedPointer<BreakpointCorrectionContext> lineCorrection;
2677 foreach (BreakpointModelId id, ids) {
2678 BreakpointParameters parameters = handler->breakpointData(id);
2679 BreakpointResponse response;
2680 response.fromParameters(parameters);
2681 response.id = BreakpointResponseId(id.majorPart(), id.minorPart());
2682 // If we encountered that file and have a module for it: Add it.
2683 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2684 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2685 if (it != m_fileNameModuleHash.constEnd())
2686 parameters.module = it.value();
2688 switch (handler->state(id)) {
2689 case BreakpointInsertRequested:
2690 if (parameters.type == BreakpointByFileAndLine) {
2691 if (lineCorrection.isNull())
2692 lineCorrection.reset(new BreakpointCorrectionContext(debuggerCore()->cppCodeModelSnapshot(),
2693 CPlusPlus::CppModelManagerInterface::instance()->workingCopy()));
2694 response.lineNumber = lineCorrection->fixLineNumber(parameters.fileName, parameters.lineNumber);
2695 postCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false), 0);
2697 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2699 if (!parameters.enabled)
2700 postCommand("bd " + QByteArray::number(id.majorPart()), 0);
2701 handler->notifyBreakpointInsertProceeding(id);
2702 handler->notifyBreakpointInsertOk(id);
2703 m_pendingBreakpointMap.insert(id, response);
2704 addedChanged = true;
2705 // Ensure enabled/disabled is correct in handler and line number is there.
2706 handler->setResponse(id, response);
2707 if (debugBreakpoints)
2708 qDebug("Adding %d %s\n", id.toInternalId(),
2709 qPrintable(response.toString()));
2711 case BreakpointChangeRequested:
2712 handler->notifyBreakpointChangeProceeding(id);
2713 if (debugBreakpoints)
2714 qDebug("Changing %d:\n %s\nTo %s\n", id.toInternalId(),
2715 qPrintable(handler->response(id).toString()),
2716 qPrintable(parameters.toString()));
2717 if (parameters.enabled != handler->response(id).enabled) {
2718 // Change enabled/disabled breakpoints without triggering update.
2719 postCommand((parameters.enabled ? "be " : "bd ")
2720 + QByteArray::number(id.majorPart()), 0);
2721 response.pending = false;
2722 response.enabled = parameters.enabled;
2723 handler->setResponse(id, response);
2725 // Delete and re-add, triggering update
2726 addedChanged = true;
2727 postCommand("bc " + QByteArray::number(id.majorPart()), 0);
2728 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2729 m_pendingBreakpointMap.insert(id, response);
2731 handler->notifyBreakpointChangeOk(id);
2733 case BreakpointRemoveRequested:
2734 postCommand("bc " + QByteArray::number(id.majorPart()), 0);
2735 handler->notifyBreakpointRemoveProceeding(id);
2736 handler->notifyBreakpointRemoveOk(id);
2737 m_pendingBreakpointMap.remove(id);
2743 // List breakpoints and send responses
2745 postCommandSequence(CommandListBreakPoints);
2748 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2749 // manager to correctly process it) and convert to clean path.
2750 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2753 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2754 if (it != m_normalizedFileCache.constEnd())
2756 if (debugSourceMapping)
2757 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2758 // Do we have source path mappings? ->Apply.
2759 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2761 // Up/lower case normalization according to Windows.
2763 QString normalized = Utils::normalizePathName(fileName);
2765 QString normalized = fileName;
2767 if (debugSourceMapping)
2768 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2769 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2770 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2771 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2773 // At least upper case drive letter if failed.
2774 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2775 result.fileName[0] = result.fileName.at(0).toUpper();
2777 m_normalizedFileCache.insert(f, result);
2778 if (debugSourceMapping)
2779 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2783 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2784 // has more processing.
2785 static StackFrames parseFrames(const GdbMi &gdbmi)
2788 const int count = gdbmi.childCount();
2790 for (int i = 0; i < count; i++) {
2791 const GdbMi &frameMi = gdbmi.childAt(i);
2794 const GdbMi fullName = frameMi.findChild("fullname");
2795 if (fullName.isValid()) {
2796 frame.file = QFile::decodeName(fullName.data());
2797 frame.line = frameMi.findChild("line").data().toInt();
2798 frame.usable = false; // To be decided after source path mapping.
2800 frame.function = QLatin1String(frameMi.findChild("func").data());
2801 frame.from = QLatin1String(frameMi.findChild("from").data());
2802 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2803 rc.push_back(frame);
2808 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2810 // Parse frames, find current. Special handling for step into:
2811 // When stepping into on an actual function (source mode) by executing 't', an assembler
2812 // frame pointing at the jmp instruction is hit (noticeable by top function being
2813 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2814 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2816 StackFrames frames = parseFrames(data);
2817 const int count = frames.size();
2818 for (int i = 0; i < count; i++) {
2819 const bool hasFile = !frames.at(i).file.isEmpty();
2820 // jmp-frame hit by step into, do another 't' and abort sequence.
2821 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2822 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2823 return ParseStackStepInto;
2826 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2827 frames[i].file = fileName.fileName;
2828 frames[i].usable = fileName.exists;
2829 if (current == -1 && frames[i].usable)
2833 if (count && current == -1) // No usable frame, use assembly.
2836 stackHandler()->setFrames(frames);
2837 activateFrame(current);
2841 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2843 if (command->success) {
2845 data.fromString(command->reply);
2846 parseStackTrace(data, false);
2847 postCommandSequence(command->commandSequence);
2849 showMessage(command->errorMessage, LogError);
2853 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2856 if (command->success) {
2857 value = command->reply.toInt();
2859 showMessage(command->errorMessage, LogError);
2861 // Is this a conditional breakpoint?
2862 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2863 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2864 const QString message = value ?
2865 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2866 arg(value).arg(cookie.id.toString()) :
2867 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2868 arg(cookie.id.toString());
2869 showMessage(message, LogMisc);
2870 // Stop if evaluation is true, else continue
2872 processStop(cookie.stopReason, true);
2874 postCommand("g", 0);
2879 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2881 if (exp.contains(' ') && !exp.startsWith('"')) {
2885 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2888 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2890 postCommandSequence(command->commandSequence);
2893 // Post a sequence of standard commands: Trigger next once one completes successfully
2894 void CdbEngine::postCommandSequence(unsigned mask)
2897 qDebug("postCommandSequence 0x%x\n", mask);
2901 if (mask & CommandListThreads) {
2902 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2905 if (mask & CommandListStack) {
2906 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2909 if (mask & CommandListRegisters) {
2910 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2911 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2914 if (mask & CommandListModules) {
2915 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2918 if (mask & CommandListBreakPoints) {
2919 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2920 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2925 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2927 bool success = false;
2930 if (!reply->success) {
2931 message = QString::fromAscii(reply->errorMessage);
2934 // Should be "namespace::QWidget:0x555"
2935 QString watchExp = QString::fromAscii(reply->reply);
2936 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2938 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2941 // 0x000 -> nothing found
2942 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2943 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2946 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2947 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2948 watchExp.insert(0, QLatin1String("*("));
2949 watchHandler()->watchExpression(watchExp);
2953 showMessage(message, LogWarning);
2954 m_watchPointX = m_watchPointY = 0;
2957 static inline void formatCdbBreakPointResponse(BreakpointModelId id, const BreakpointResponse &r,
2960 str << "Obtained breakpoint " << id << " (#" << r.id.majorPart() << ')';
2964 str.setIntegerBase(16);
2965 str << ", at 0x" << r.address;
2966 str.setIntegerBase(10);
2969 str << ", disabled";
2970 if (!r.module.isEmpty())
2971 str << ", module: '" << r.module << '\'';
2975 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2977 if (debugBreakpoints)
2978 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2979 if (!reply->success) {
2980 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2984 value.fromString(reply->reply);
2985 if (value.type() != GdbMi::List) {
2986 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2989 handleBreakPoints(value);
2992 void CdbEngine::handleBreakPoints(const GdbMi &value)
2994 // Report all obtained parameters back. Note that not all parameters are reported
2995 // back, so, match by id and complete
2996 if (debugBreakpoints)
2997 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2999 QTextStream str(&message);
3000 BreakHandler *handler = breakHandler();
3001 foreach (const GdbMi &breakPointG, value.children()) {
3002 BreakpointResponse reportedResponse;
3003 parseBreakPoint(breakPointG, &reportedResponse);
3004 if (debugBreakpoints)
3005 qDebug(" Parsed %d: pending=%d %s\n", reportedResponse.id.majorPart(),
3006 reportedResponse.pending,
3007 qPrintable(reportedResponse.toString()));
3008 if (reportedResponse.id.isValid() && !reportedResponse.pending) {
3009 const BreakpointModelId mid = handler->findBreakpointByResponseId(reportedResponse.id);
3010 QTC_ASSERT(mid.isValid(), continue; )
3011 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(mid);
3012 if (it != m_pendingBreakpointMap.end()) {
3013 // Complete the response and set on handler.
3014 BreakpointResponse ¤tResponse = it.value();
3015 currentResponse.id = reportedResponse.id;
3016 currentResponse.address = reportedResponse.address;
3017 currentResponse.module = reportedResponse.module;
3018 currentResponse.pending = reportedResponse.pending;
3019 currentResponse.enabled = reportedResponse.enabled;
3020 formatCdbBreakPointResponse(mid, currentResponse, str);
3021 if (debugBreakpoints)
3022 qDebug(" Setting for %d: %s\n", currentResponse.id.majorPart(),
3023 qPrintable(currentResponse.toString()));
3024 handler->setResponse(mid, currentResponse);
3025 m_pendingBreakpointMap.erase(it);
3027 } // not pending reported
3029 if (m_pendingBreakpointMap.empty()) {
3030 str << QLatin1String("All breakpoints have been resolved.\n");
3032 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
3034 showMessage(message, LogMisc);
3037 void CdbEngine::watchPoint(const QPoint &p)
3039 m_watchPointX = p.x();
3040 m_watchPointY = p.y();
3042 case InferiorStopOk:
3043 postWidgetAtCommand();
3046 // "Select Widget to Watch" from a running application is currently not
3047 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
3048 // but requires some work as not to confuse the engine by state-change notifications
3049 // emitted by the debuggee function call.
3050 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
3053 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
3054 arg(QString::fromAscii(stateName(state()))), LogWarning);
3059 void CdbEngine::postWidgetAtCommand()
3061 QByteArray arguments = QByteArray::number(m_watchPointX);
3062 arguments.append(' ');
3063 arguments.append(QByteArray::number(m_watchPointY));
3064 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
3067 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
3069 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
3070 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
3071 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
3074 if (qVariantCanConvert<MemoryViewCookie>(v)) {
3075 postFetchMemory(qVariantValue<MemoryViewCookie>(v));
3080 } // namespace Internal
3081 } // namespace Debugger