1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "cdbengine.h"
35 #include "debuggerstartparameters.h"
36 #include "disassemblerlines.h"
37 #include "cdboptions.h"
38 #include "cdboptionspage.h"
39 #include "bytearrayinputstream.h"
40 #include "breakpoint.h"
41 #include "breakhandler.h"
42 #include "stackframe.h"
43 #include "stackhandler.h"
44 #include "watchhandler.h"
45 #include "threadshandler.h"
46 #include "moduleshandler.h"
47 #include "debuggeractions.h"
48 #include "debuggercore.h"
49 #include "registerhandler.h"
50 #include "disassembleragent.h"
51 #include "memoryagent.h"
52 #include "debuggerrunner.h"
53 #include "debuggertooltipmanager.h"
54 #include "cdbparsehelpers.h"
55 #include "watchutils.h"
56 #include "gdb/gdbmi.h"
57 #include "shared/cdbsymbolpathlisteditor.h"
59 #include <coreplugin/icore.h>
60 #include <texteditor/itexteditor.h>
61 #include <projectexplorer/abi.h>
62 #include <projectexplorer/projectexplorerconstants.h>
64 #include <utils/synchronousprocess.h>
65 #include <utils/winutils.h>
66 #include <utils/qtcassert.h>
67 #include <utils/savedaction.h>
68 #include <utils/consoleprocess.h>
70 #include <QtCore/QCoreApplication>
71 #include <QtCore/QFileInfo>
72 #include <QtCore/QDir>
73 #include <QtCore/QDebug>
74 #include <QtCore/QTextStream>
75 #include <QtCore/QDateTime>
76 #include <QtGui/QToolTip>
77 #include <QtGui/QMainWindow>
78 #include <QtGui/QMessageBox>
81 # include <utils/winutils.h>
82 # include "dbgwinutils.h"
87 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
88 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
91 enum { debugLocals = 0 };
92 enum { debugSourceMapping = 0 };
93 enum { debugWatches = 0 };
94 enum { debugBreakpoints = 0 };
97 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
99 # define STATE_DEBUG(state, func, line, notifyFunc)
103 \class Debugger::Internal::CdbEngine
105 Cdb engine version 2: Run the CDB process on pipes and parse its output.
106 The engine relies on a CDB extension Qt Creator provides as an extension
107 library (32/64bit), which is loaded into cdb.exe. It serves to:
110 \o Notify the engine about the state of the debugging session:
112 \o idle: (hooked up with .idle_cmd) debuggee stopped
113 \o accessible: Debuggee stopped, cdb.exe accepts commands
114 \o inaccessible: Debuggee runs, no way to post commands
115 \o session active/inactive: Lost debuggee, terminating.
117 \o Hook up with output/event callbacks and produce formatted output to be able
118 to catch application output and exceptions.
119 \o Provide some extension commands that produce output in a standardized (GDBMI)
120 format that ends up in handleExtensionMessage(), for example:
122 \o pid Return debuggee pid for interrupting.
123 \o locals Print locals from SymbolGroup
124 \o expandLocals Expand locals in symbol group
125 \o registers, modules, threads
129 Debugger commands can be posted by calling:
133 \o postCommand(): Does not expect a reply
134 \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
135 that is captured by enclosing it in special tokens using the 'echo' command and
136 then invokes a callback with a CdbBuiltinCommand structure.
137 \o postExtensionCommand(): Run a command provided by the extension producing
138 one-line output and invoke a callback with a CdbExtensionCommand structure
139 (output is potentially split up in chunks).
144 [Console: The console stub launches the process. On process startup,
145 launchCDB() is called with AttachExternal].
146 setupEngine() calls launchCDB() with the startparameters. The debuggee
147 runs into the initial breakpoint (session idle). EngineSetupOk is
148 notified (inferior still stopped). setupInferior() is then called
149 which does breakpoint synchronization and issues the extension 'pid'
150 command to obtain the inferior pid (which also hooks up the output callbacks).
151 handlePid() notifies notifyInferiorSetupOk.
152 runEngine() is then called which issues 'g' to continue the inferior.
153 Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
154 when the inferior exits (except attach modes).
157 using namespace ProjectExplorer;
162 static const char localsPrefixC[] = "local.";
164 struct MemoryViewCookie
166 explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
167 quint64 addr = 0, quint64 l = 0) :
168 agent(a), editorToken(e), address(addr), length(l)
172 QObject *editorToken;
177 struct MemoryChangeCookie
179 explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
180 address(addr), data(d) {}
186 } // namespace Internal
187 } // namespace Debugger
189 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
190 Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
195 static inline bool isConsole(const DebuggerStartParameters &sp)
197 return (sp.startMode == StartInternal || sp.startMode == StartExternal)
202 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
204 QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
205 debuggerCore()->mainWindow());
206 mb->setAttribute(Qt::WA_DeleteOnClose);
211 // Base data structure for command queue entries with callback
212 struct CdbCommandBase
214 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
217 CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
218 unsigned nc, const QVariant &cookie);
224 // Continue with another commands as specified in CommandSequenceFlags
225 unsigned commandSequence;
228 CdbCommandBase::CdbCommandBase() :
229 token(0), flags(0), commandSequence(0)
233 CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
234 unsigned nc, const QVariant &c) :
235 token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
239 // Queue entry for builtin commands producing free-format
240 // line-by-line output.
241 struct CdbBuiltinCommand : public CdbCommandBase
243 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
245 CdbBuiltinCommand() {}
246 CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
248 unsigned nc, const QVariant &cookie) :
249 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
253 QByteArray joinedReply() const;
255 CommandHandler handler;
256 QList<QByteArray> reply;
259 QByteArray CdbBuiltinCommand::joinedReply() const
264 answer.reserve(120 * reply.size());
265 foreach (const QByteArray &l, reply) {
272 // Queue entry for Qt Creator extension commands producing one-line
273 // output with success flag and error message.
274 struct CdbExtensionCommand : public CdbCommandBase
276 typedef CdbEngine::ExtensionCommandHandler CommandHandler;
278 CdbExtensionCommand() : success(false) {}
279 CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
281 unsigned nc, const QVariant &cookie) :
282 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
284 CommandHandler handler;
286 QByteArray errorMessage;
290 template <class CommandPtrType>
291 int indexOfCommand(const QList<CommandPtrType> &l, int token)
293 const int count = l.size();
294 for (int i = 0; i < count; i++)
295 if (l.at(i)->token == token)
300 static inline bool validMode(DebuggerStartMode sm)
314 // Accessed by RunControlFactory
315 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
316 DebuggerEngine *masterEngine, QString *errorMessage)
319 CdbOptionsPage *op = CdbOptionsPage::instance();
320 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
321 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
324 return new CdbEngine(sp, masterEngine, op->options());
326 Q_UNUSED(masterEngine)
329 *errorMessage = QString::fromLatin1("Unsupported debug mode");
333 bool isCdbEngineEnabled()
336 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
342 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
344 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
347 static QString cdbBinary(const DebuggerStartParameters &sp)
349 if (!sp.debuggerCommand.isEmpty()) {
350 // Do not use a GDB binary if we got started for a project with MinGW runtime.
351 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
352 && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
353 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
354 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
356 return sp.debuggerCommand;
358 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
361 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
364 if (!isCdbEngineEnabled()) {
365 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
366 arg(sp.toolChainAbi.toString()));
367 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
368 check->settingsPage = CdbOptionsPage::settingsId();
372 if (!validMode(sp.startMode)) {
373 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
377 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
378 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
379 arg(sp.toolChainAbi.toString()));
383 if (cdbBinary(sp).isEmpty()) {
384 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
385 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
386 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
393 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
398 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
401 opts->push_back(new CdbOptionsPage);
407 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
409 static inline Utils::SavedAction *theAssemblerAction()
411 return debuggerCore()->action(OperateByInstruction);
414 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
415 DebuggerEngine *masterEngine, const OptionsPtr &options) :
416 DebuggerEngine(sp, masterEngine),
417 m_creatorExtPrefix("<qtcreatorcdbext>|"),
418 m_tokenPrefix("<token>"),
420 m_effectiveStartMode(NoStartMode),
423 m_specialStopMode(NoSpecialStop),
424 m_nextCommandToken(0),
425 m_currentBuiltinCommandIndex(-1),
426 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
427 m_operateByInstructionPending(true),
428 m_operateByInstruction(true), // Default CDB setting
429 m_notifyEngineShutdownOnTermination(false),
430 m_hasDebuggee(false),
432 m_sourceStepInto(false),
435 m_ignoreCdbOutput(false)
437 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
439 setObjectName(QLatin1String("CdbEngine"));
440 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
441 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
442 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
443 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
446 void CdbEngine::init()
448 m_effectiveStartMode = NoStartMode;
450 m_accessible = false;
451 m_specialStopMode = NoSpecialStop;
452 m_nextCommandToken = 0;
453 m_currentBuiltinCommandIndex = -1;
454 m_operateByInstructionPending = theAssemblerAction()->isChecked();
455 m_operateByInstruction = true; // Default CDB setting
456 m_notifyEngineShutdownOnTermination = false;
457 m_hasDebuggee = false;
458 m_sourceStepInto = false;
459 m_watchPointX = m_watchPointY = 0;
460 m_ignoreCdbOutput = false;
462 m_outputBuffer.clear();
463 m_builtinCommandQueue.clear();
464 m_extensionCommandQueue.clear();
465 m_extensionMessageBuffer.clear();
466 m_pendingBreakpointMap.clear();
467 m_customSpecialStopData.clear();
469 // Create local list of mappings in native separators
470 m_sourcePathMappings.clear();
471 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
472 if (!globalOptions->sourcePathMap.isEmpty()) {
473 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
474 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
475 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
476 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
477 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
478 QDir::toNativeSeparators(it.value())));
481 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
484 CdbEngine::~CdbEngine()
488 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
490 if (state() == InferiorStopOk) {
491 syncOperateByInstruction(operateByInstruction);
493 // To be set next time session becomes accessible
494 m_operateByInstructionPending = operateByInstruction;
498 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
501 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
502 if (m_operateByInstruction == operateByInstruction)
504 QTC_ASSERT(m_accessible, return; )
505 m_operateByInstruction = operateByInstruction;
506 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
507 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
510 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
511 TextEditor::ITextEditor *editor,
512 const DebuggerToolTipContext &contextIn)
515 qDebug() << Q_FUNC_INFO;
516 // Need a stopped debuggee and a cpp file in a valid frame
517 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
519 // Determine expression and function
522 DebuggerToolTipContext context = contextIn;
523 const QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
524 // Are we in the current stack frame
525 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
527 // No numerical or any other expressions [yet]
528 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
530 const QByteArray iname = QByteArray(localsPrefixC) + exp.toAscii();
531 const QModelIndex index = watchHandler()->itemIndex(iname);
532 if (!index.isValid())
534 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
535 tw->setContext(context);
536 tw->setDebuggerModel(LocalsWatch);
537 tw->setExpression(exp);
538 tw->acquireEngine(this);
539 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
543 // Determine full path to the CDB extension library.
544 QString CdbEngine::extensionLibraryName(bool is64Bit)
546 // Determine extension lib name and path to use
548 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
549 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
550 << '/' << QT_CREATOR_CDB_EXT << ".dll";
554 // Determine environment for CDB.exe, start out with run config and
555 // add CDB extension path merged with system value should there be one.
556 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
557 QString cdbExtensionPath)
559 // Determine CDB extension path from Qt Creator
560 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
561 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
562 if (!oldCdbExtensionPath.isEmpty()) {
563 cdbExtensionPath.append(QLatin1Char(';'));
564 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
566 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
567 // config, just to make sure, delete any existing entries
568 const QString cdbExtensionPathVariableAssign =
569 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
570 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
571 if (it->startsWith(cdbExtensionPathVariableAssign)) {
572 it = runConfigEnvironment.erase(it);
578 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
579 QDir::toNativeSeparators(cdbExtensionPath));
580 return runConfigEnvironment;
583 int CdbEngine::elapsedLogTime() const
585 const int elapsed = m_logTime.elapsed();
586 const int delta = elapsed - m_elapsedLogTime;
587 m_elapsedLogTime = elapsed;
591 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
592 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
595 qDebug("startConsole %s", qPrintable(sp.executable));
596 m_consoleStub.reset(new Utils::ConsoleProcess);
597 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
598 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
599 SLOT(consoleStubMessage(QString, bool)));
600 connect(m_consoleStub.data(), SIGNAL(processStarted()),
601 SLOT(consoleStubProcessStarted()));
602 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
603 SLOT(consoleStubExited()));
604 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
605 if (sp.environment.size())
606 m_consoleStub->setEnvironment(sp.environment);
607 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
608 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
614 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
617 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
619 if (state() == EngineSetupRequested) {
620 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
621 notifyEngineSetupFailed();
623 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
626 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
628 showMessage(msg, AppOutput);
632 void CdbEngine::consoleStubProcessStarted()
635 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
636 // Attach to console process.
637 DebuggerStartParameters attachParameters = startParameters();
638 attachParameters.executable.clear();
639 attachParameters.processArgs.clear();
640 attachParameters.attachPID = m_consoleStub->applicationPID();
641 attachParameters.startMode = AttachExternal;
642 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
643 QString errorMessage;
644 if (!launchCDB(attachParameters, &errorMessage)) {
645 showMessage(errorMessage, LogError);
646 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
647 notifyEngineSetupFailed();
651 void CdbEngine::consoleStubExited()
655 void CdbEngine::setupEngine()
658 qDebug(">setupEngine");
659 // Nag to add symbol server
660 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
661 &(m_options->symbolPaths)))
662 m_options->toSettings(Core::ICore::instance()->settings());
665 if (!m_logTime.elapsed())
667 QString errorMessage;
668 // Console: Launch the stub with the suspended application and attach to it
669 // CDB in theory has a command line option '-2' that launches a
670 // console, too, but that immediately closes when the debuggee quits.
671 // Use the Creator stub instead.
672 const DebuggerStartParameters &sp = startParameters();
673 const bool launchConsole = isConsole(sp);
674 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
675 const bool ok = launchConsole ?
676 startConsole(startParameters(), &errorMessage) :
677 launchCDB(startParameters(), &errorMessage);
679 qDebug("<setupEngine ok=%d", ok);
681 showMessage(errorMessage, LogError);
682 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
683 notifyEngineSetupFailed();
687 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
690 qDebug("launchCDB startMode=%d", sp.startMode);
691 const QChar blank(QLatin1Char(' '));
692 // Start engine which will run until initial breakpoint:
693 // Determine binary (force MSVC), extension lib name and path to use
694 // The extension is passed as relative name with the path variable set
695 //(does not work with absolute path names)
696 const QString executable = cdbBinary(sp);
697 if (executable.isEmpty()) {
698 *errorMessage = tr("There is no CDB executable specified.");
704 Utils::winIs64BitBinary(executable);
708 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
709 if (!extensionFi.isFile()) {
710 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
711 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
714 const QString extensionFileName = extensionFi.fileName();
716 QStringList arguments;
717 const bool isRemote = sp.startMode == AttachToRemote;
718 if (isRemote) { // Must be first
719 arguments << QLatin1String("-remote") << sp.remoteChannel;
721 arguments << (QLatin1String("-a") + extensionFileName);
723 // Source line info/No terminal breakpoint / Pull extension
724 arguments << QLatin1String("-lines") << QLatin1String("-G")
725 // register idle (debuggee stop) notification
726 << QLatin1String("-c")
727 << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
728 if (sp.useTerminal) // Separate console
729 arguments << QLatin1String("-2");
730 if (!m_options->symbolPaths.isEmpty())
731 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
732 if (!m_options->sourcePaths.isEmpty())
733 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
734 // Compile argument string preserving quotes
735 QString nativeArguments = m_options->additionalArguments;
736 switch (sp.startMode) {
739 if (!nativeArguments.isEmpty())
740 nativeArguments.push_back(blank);
741 nativeArguments += QDir::toNativeSeparators(sp.executable);
746 case AttachCrashedExternal:
747 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
748 if (sp.startMode == AttachCrashedExternal)
749 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
752 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
755 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
756 if (!nativeArguments.isEmpty())
757 nativeArguments.push_back(blank);
758 nativeArguments += sp.processArgs;
761 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
762 arg(QDir::toNativeSeparators(executable),
763 arguments.join(QString(blank)) + blank + nativeArguments,
764 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
765 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
766 showMessage(msg, LogMisc);
768 m_outputBuffer.clear();
769 const QStringList environment = sp.environment.size() == 0 ?
770 QProcessEnvironment::systemEnvironment().toStringList() :
771 sp.environment.toStringList();
772 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
773 if (!sp.workingDirectory.isEmpty())
774 m_process.setWorkingDirectory(sp.workingDirectory);
777 if (!nativeArguments.isEmpty()) // Appends
778 m_process.setNativeArguments(nativeArguments);
780 m_process.start(executable, arguments);
781 if (!m_process.waitForStarted()) {
782 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
783 arg(QDir::toNativeSeparators(executable), m_process.errorString());
787 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
789 const unsigned long pid = 0;
791 showMessage(QString::fromLatin1("%1 running as %2").
792 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
793 m_hasDebuggee = true;
794 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
796 const QByteArray loadCommand = QByteArray(".load ")
797 + extensionFileName.toLocal8Bit();
798 postCommand(loadCommand, 0);
799 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
800 notifyEngineSetupOk();
805 void CdbEngine::setupInferior()
808 qDebug("setupInferior");
809 attemptBreakpointSynchronization();
810 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
811 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
814 void CdbEngine::runEngine()
818 // Resume the threads frozen by the console stub.
819 if (isConsole(startParameters()))
820 postCommand("~* m", 0);
821 foreach (const QString &breakEvent, m_options->breakEvents)
822 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
826 bool CdbEngine::commandsPending() const
828 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
831 void CdbEngine::shutdownInferior()
834 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
835 isCdbProcessRunning());
837 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
839 qDebug("notifyInferiorShutdownOk");
840 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
841 notifyInferiorShutdownOk();
846 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
848 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
849 notifyInferiorShutdownOk();
851 // A command got stuck.
852 if (commandsPending()) {
853 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
854 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
855 notifyInferiorShutdownFailed();
858 if (!canInterruptInferior()) {
859 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
860 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
861 notifyInferiorShutdownFailed();
864 interruptInferior(); // Calls us again
868 /* shutdownEngine/processFinished:
869 * Note that in the case of launching a process by the debugger, the debugger
870 * automatically quits a short time after reporting the session becoming
871 * inaccessible without debuggee (notifyInferiorExited). In that case,
872 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
873 * as not to confuse the state engine.
876 void CdbEngine::shutdownEngine()
879 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
880 "accessible=%d,commands pending=%d",
881 stateName(state()), isCdbProcessRunning(), m_accessible,
884 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
885 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
886 notifyEngineShutdownOk();
890 // No longer trigger anything from messages
891 m_ignoreCdbOutput = true;
892 // Go for kill if there are commands pending.
893 if (m_accessible && !commandsPending()) {
894 // detach: Wait for debugger to finish.
895 if (m_effectiveStartMode == AttachExternal)
897 // Remote requires a bit more force to quit.
898 if (m_effectiveStartMode == AttachToRemote) {
899 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
900 postCommand("qq", 0);
904 m_notifyEngineShutdownOnTermination = true;
907 // Remote process. No can do, currently
908 m_notifyEngineShutdownOnTermination = true;
909 Utils::SynchronousProcess::stopProcess(m_process);
912 // Lost debuggee, debugger should quit anytime now
913 if (!m_hasDebuggee) {
914 m_notifyEngineShutdownOnTermination = true;
920 void CdbEngine::processFinished()
923 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
924 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
925 m_process.exitStatus(), m_process.exitCode());
927 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
929 showMessage(tr("CDB crashed"), LogError); // not in your life.
931 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
934 if (m_notifyEngineShutdownOnTermination) {
937 qDebug("notifyEngineIll");
938 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
941 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
942 notifyEngineShutdownOk();
945 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
946 // Otherwise, we take a shortcut.
947 if (isSlaveEngine()) {
948 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
949 notifyInferiorExited();
951 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
952 notifyEngineSpontaneousShutdown();
957 void CdbEngine::detachDebugger()
959 postCommand(".detach", 0);
962 static inline bool isWatchIName(const QByteArray &iname)
964 return iname.startsWith("watch");
967 void CdbEngine::updateWatchData(const WatchData &dataIn,
968 const WatchUpdateFlags & flags)
970 if (debug || debugLocals || debugWatches)
971 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
972 elapsedLogTime(), m_accessible, stateName(state()),
973 flags.tryIncremental,
974 qPrintable(dataIn.toString()));
976 if (!m_accessible) // Add watch data while running?
980 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
982 ByteArrayInputStream str(args);
983 str << dataIn.iname << " \"" << dataIn.exp << '"';
984 postExtensionCommand("addwatch", args, 0,
985 &CdbEngine::handleAddWatch, 0,
986 qVariantFromValue(dataIn));
990 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
991 WatchData data = dataIn;
992 data.setAllUnneeded();
993 watchHandler()->insertData(data);
996 updateLocalVariable(dataIn.iname);
999 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1001 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1003 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1004 if (reply->success) {
1005 updateLocalVariable(item.iname);
1007 item.setError(tr("Unable to add expression"));
1008 watchHandler()->insertData(item);
1009 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1010 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1011 reply->errorMessage), LogError);
1015 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1017 if (debuggerCore()->boolSetting(VerboseLog))
1018 str << blankSeparator << "-v";
1019 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1020 str << blankSeparator << "-c";
1021 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1022 if (!typeFormats.isEmpty())
1023 str << blankSeparator << "-T " << typeFormats;
1024 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1025 if (!individualFormats.isEmpty())
1026 str << blankSeparator << "-I " << individualFormats;
1029 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1031 const bool isWatch = isWatchIName(iname);
1033 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1034 QByteArray localsArguments;
1035 ByteArrayInputStream str(localsArguments);
1036 addLocalsOptions(str);
1038 const int stackFrame = stackHandler()->currentIndex();
1039 if (stackFrame < 0) {
1040 qWarning("Internal error; no stack frame in updateLocalVariable");
1043 str << blankSeparator << stackFrame;
1045 str << blankSeparator << iname;
1046 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1049 unsigned CdbEngine::debuggerCapabilities() const
1051 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1052 |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
1053 |ReloadModuleCapability
1054 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1055 |BreakModuleCapability;
1058 void CdbEngine::executeStep()
1060 if (!m_operateByInstruction)
1061 m_sourceStepInto = true; // See explanation at handleStackTrace().
1062 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1063 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1064 notifyInferiorRunRequested();
1067 void CdbEngine::executeStepOut()
1069 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1070 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1071 notifyInferiorRunRequested();
1074 void CdbEngine::executeNext()
1076 postCommand(QByteArray("p"), 0); // Step over -> p
1077 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1078 notifyInferiorRunRequested();
1081 void CdbEngine::executeStepI()
1086 void CdbEngine::executeNextI()
1091 void CdbEngine::continueInferior()
1093 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1094 notifyInferiorRunRequested();
1095 doContinueInferior();
1098 void CdbEngine::doContinueInferior()
1100 postCommand(QByteArray("g"), 0);
1103 bool CdbEngine::canInterruptInferior() const
1105 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1108 void CdbEngine::interruptInferior()
1111 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1112 if (canInterruptInferior()) {
1113 doInterruptInferior(NoSpecialStop);
1115 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1116 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1117 notifyInferiorStopOk();
1118 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1119 notifyInferiorRunRequested();
1120 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1121 notifyInferiorRunOk();
1125 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1127 if (m_specialStopMode == NoSpecialStop)
1128 doInterruptInferior(CustomSpecialStop);
1129 m_customSpecialStopData.push_back(v);
1132 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1135 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1136 m_specialStopMode = sm;
1137 QString errorMessage;
1138 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1139 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1140 m_specialStopMode = oldSpecialMode;
1141 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1148 void CdbEngine::executeRunToLine(const ContextData &data)
1150 // Add one-shot breakpoint
1151 BreakpointParameters bp;
1153 bp.type =BreakpointByAddress;
1154 bp.address = data.address;
1156 bp.type =BreakpointByFileAndLine;
1157 bp.fileName = data.fileName;
1158 bp.lineNumber = data.lineNumber;
1160 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1164 void CdbEngine::executeRunToFunction(const QString &functionName)
1166 // Add one-shot breakpoint
1167 BreakpointParameters bp(BreakpointByFunction);
1168 bp.functionName = functionName;
1170 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1174 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1176 const Registers registers = registerHandler()->registers();
1177 QTC_ASSERT(regnr < registers.size(), return)
1178 // Value is decimal or 0x-hex-prefixed
1180 ByteArrayInputStream str(cmd);
1181 str << "r " << registers.at(regnr).name << '=' << value;
1182 postCommand(cmd, 0);
1186 void CdbEngine::executeJumpToLine(const ContextData &data)
1189 // Goto address directly.
1190 jumpToAddress(data.address);
1191 gotoLocation(Location(data.address));
1193 // Jump to source line: Resolve source line address and go to that location
1195 ByteArrayInputStream str(cmd);
1196 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1197 const QVariant cookie = qVariantFromValue(data);
1198 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1202 void CdbEngine::jumpToAddress(quint64 address)
1204 // Fake a jump to address by setting the PC register.
1205 QByteArray registerCmd;
1206 ByteArrayInputStream str(registerCmd);
1207 // PC-register depending on 64/32bit.
1208 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1209 str.setHexPrefix(true);
1210 str.setIntegerBase(16);
1212 postCommand(registerCmd, 0);
1215 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1217 if (cmd->reply.isEmpty())
1219 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1220 // Set register 'rip' to hex address and goto lcoation
1221 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1222 const int equalPos = answer.indexOf(" = ");
1225 answer.remove(0, equalPos + 3);
1226 answer.remove(QLatin1Char('`'));
1228 const quint64 address = answer.toLongLong(&ok, 16);
1229 if (ok && address) {
1230 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1231 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1232 jumpToAddress(address);
1233 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1237 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1240 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1242 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1243 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1248 ByteArrayInputStream str(cmd);
1249 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1250 postCommand(cmd, 0);
1251 // Update all locals in case we change a union or something pointed to
1252 // that affects other variables, too.
1256 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1258 int currentThreadId;
1259 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1260 threadsHandler()->setThreads(threads);
1261 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1262 forceCurrentThreadId : currentThreadId);
1265 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1268 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1269 if (reply->success) {
1271 data.fromString(reply->reply);
1273 // Continue sequence
1274 postCommandSequence(reply->commandSequence);
1276 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1280 void CdbEngine::executeDebuggerCommand(const QString &command)
1282 postCommand(command.toLocal8Bit(), QuietCommand);
1285 // Post command without callback
1286 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1289 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1290 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1291 if (!(flags & QuietCommand))
1292 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1293 m_process.write(cmd + '\n');
1296 // Post a built-in-command producing free-format output with a callback.
1297 // In order to catch the output, it is enclosed in 'echo' commands
1298 // printing a specially formatted token to be identifiable in the output.
1299 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1300 BuiltinCommandHandler handler,
1301 unsigned nextCommandFlag,
1302 const QVariant &cookie)
1304 if (!m_accessible) {
1305 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1306 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1307 showMessage(msg, LogError);
1310 if (!flags & QuietCommand)
1311 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1313 const int token = m_nextCommandToken++;
1314 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1316 m_builtinCommandQueue.push_back(pendingCommand);
1317 // Enclose command in echo-commands for token
1319 ByteArrayInputStream str(fullCmd);
1320 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1321 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1323 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1324 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1325 m_builtinCommandQueue.size(), nextCommandFlag);
1327 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1328 fullCmd.constData());
1329 m_process.write(fullCmd);
1332 // Post an extension command producing one-line output with a callback,
1333 // pass along token for identification in queue.
1334 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1335 const QByteArray &arguments,
1337 ExtensionCommandHandler handler,
1338 unsigned nextCommandFlag,
1339 const QVariant &cookie)
1341 if (!m_accessible) {
1342 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1343 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1344 showMessage(msg, LogError);
1348 const int token = m_nextCommandToken++;
1350 // Format full command with token to be recognizeable in the output
1352 ByteArrayInputStream str(fullCmd);
1353 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1354 if (!arguments.isEmpty())
1355 str << ' ' << arguments;
1357 if (!flags & QuietCommand)
1358 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1360 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1362 m_extensionCommandQueue.push_back(pendingCommand);
1363 // Enclose command in echo-commands for token
1365 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1366 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1367 m_extensionCommandQueue.size(), nextCommandFlag);
1368 m_process.write(fullCmd + '\n');
1371 void CdbEngine::activateFrame(int index)
1373 // TODO: assembler,etc
1376 const StackFrames &frames = stackHandler()->frames();
1377 QTC_ASSERT(index < frames.size(), return; )
1379 const StackFrame frame = frames.at(index);
1380 if (debug || debugLocals)
1381 qDebug("activateFrame idx=%d '%s' %d", index,
1382 qPrintable(frame.file), frame.line);
1383 stackHandler()->setCurrentIndex(index);
1384 const bool showAssembler = !frames.at(index).isUsable();
1385 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1386 watchHandler()->beginCycle();
1387 watchHandler()->endCycle();
1388 QAction *assemblerAction = theAssemblerAction();
1389 if (assemblerAction->isChecked()) {
1390 gotoLocation(frame);
1392 assemblerAction->trigger(); // Seems to trigger update
1395 gotoLocation(frame);
1400 void CdbEngine::updateLocals(bool forNewStackFrame)
1402 typedef QHash<QByteArray, int> WatcherHash;
1404 const int frameIndex = stackHandler()->currentIndex();
1405 if (frameIndex < 0) {
1406 watchHandler()->beginCycle();
1407 watchHandler()->endCycle();
1410 const StackFrame frame = stackHandler()->currentFrame();
1411 if (!frame.isUsable()) {
1412 watchHandler()->beginCycle();
1413 watchHandler()->endCycle();
1416 /* Watchers: Forcibly discard old symbol group as switching from
1417 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1418 * and cause errors as it seems to go 'stale' when switching threads.
1419 * Initial expand, get uninitialized and query */
1420 QByteArray arguments;
1421 ByteArrayInputStream str(arguments);
1424 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1425 if (!expanded.isEmpty()) {
1426 str << blankSeparator << "-e ";
1428 foreach(const QByteArray &e, expanded) {
1434 addLocalsOptions(str);
1435 // Uninitialized variables if desired. Quote as safeguard against shadowed
1436 // variables in case of errors in uninitializedVariables().
1437 if (debuggerCore()->boolSetting(UseCodeModel)) {
1438 QStringList uninitializedVariables;
1439 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1440 frame.function, frame.file, frame.line, &uninitializedVariables);
1441 if (!uninitializedVariables.isEmpty()) {
1442 str << blankSeparator << "-u \"";
1444 foreach(const QString &u, uninitializedVariables) {
1447 str << localsPrefixC << u;
1452 // Perform watches synchronization
1453 str << blankSeparator << "-W";
1454 const WatcherHash watcherHash = WatchHandler::watcherNames();
1455 if (!watcherHash.isEmpty()) {
1456 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1457 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1458 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1462 // Required arguments: frame
1463 str << blankSeparator << frameIndex;
1464 watchHandler()->beginCycle();
1465 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1468 void CdbEngine::selectThread(int index)
1470 if (index < 0 || index == threadsHandler()->currentThread())
1474 const int newThreadId = threadsHandler()->threads().at(index).id;
1475 threadsHandler()->setCurrentThread(index);
1477 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1478 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1481 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1483 QTC_ASSERT(m_accessible, return;)
1485 ByteArrayInputStream str(cmd);
1486 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1487 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1488 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1491 // Parse: "00000000`77606060 cc int 3"
1492 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1494 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1495 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1496 DisassemblerLines disassemblerLines;
1497 foreach(const QByteArray &line, command->reply)
1498 disassemblerLines.appendLine(DisassemblerLine(QString::fromLatin1(line)));
1499 agent->setContents(disassemblerLines);
1502 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1504 if (!m_accessible) {
1505 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1509 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1512 ByteArrayInputStream str(args);
1513 str << addr << ' ' << length;
1514 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1515 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1518 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1520 QTC_ASSERT(!data.isEmpty(), return; )
1521 if (!m_accessible) {
1522 const MemoryChangeCookie cookie(addr, data);
1523 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1525 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1529 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1531 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1532 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1533 if (command->success) {
1534 const QByteArray data = QByteArray::fromBase64(command->reply);
1535 if (unsigned(data.size()) == memViewCookie.length)
1536 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1537 memViewCookie.address, data);
1539 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1543 void CdbEngine::reloadModules()
1545 postCommandSequence(CommandListModules);
1548 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1552 void CdbEngine::loadAllSymbols()
1556 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1558 Q_UNUSED(moduleName)
1561 void CdbEngine::reloadRegisters()
1563 postCommandSequence(CommandListRegisters);
1566 void CdbEngine::reloadSourceFiles()
1570 void CdbEngine::reloadFullStack()
1573 qDebug("%s", Q_FUNC_INFO);
1574 postCommandSequence(CommandListStack);
1577 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1579 if (reply->success) {
1580 m_inferiorPid = reply->reply.toUInt();
1581 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1582 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1583 notifyInferiorSetupOk();
1585 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1586 arg(QLatin1String(reply->errorMessage)), LogError);
1587 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1588 notifyInferiorSetupFailed();
1592 // Parse CDB gdbmi register syntax
1593 static inline Register parseRegister(const GdbMi &gdbmiReg)
1596 reg.name = gdbmiReg.findChild("name").data();
1597 const GdbMi description = gdbmiReg.findChild("description");
1598 if (description.type() != GdbMi::Invalid) {
1600 reg.name += description.data();
1603 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1607 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1609 if (reply->success) {
1611 value.fromString(reply->reply);
1612 if (value.type() == GdbMi::List) {
1614 modules.reserve(value.childCount());
1615 foreach (const GdbMi &gdbmiModule, value.children()) {
1617 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1618 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1619 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1620 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1621 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1622 module.symbolsRead = Module::ReadOk;
1623 modules.push_back(module);
1625 modulesHandler()->setModules(modules);
1627 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1628 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1631 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1632 arg(QLatin1String(reply->errorMessage)), LogError);
1634 postCommandSequence(reply->commandSequence);
1638 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1640 if (reply->success) {
1642 value.fromString(reply->reply);
1643 if (value.type() == GdbMi::List) {
1644 Registers registers;
1645 registers.reserve(value.childCount());
1646 foreach (const GdbMi &gdbmiReg, value.children())
1647 registers.push_back(parseRegister(gdbmiReg));
1648 registerHandler()->setRegisters(registers);
1650 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1651 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1654 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1655 arg(QLatin1String(reply->errorMessage)), LogError);
1657 postCommandSequence(reply->commandSequence);
1660 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1662 if (reply->success) {
1663 QList<WatchData> watchData;
1665 root.fromString(reply->reply);
1666 QTC_ASSERT(root.isList(), return ; )
1668 qDebug() << root.toString(true, 4);
1670 // Courtesy of GDB engine
1671 foreach (const GdbMi &child, root.children()) {
1673 dummy.iname = child.findChild("iname").data();
1674 dummy.name = QLatin1String(child.findChild("name").data());
1675 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1677 watchHandler()->insertBulkData(watchData);
1678 watchHandler()->endCycle();
1680 QDebug nsp = qDebug().nospace();
1681 nsp << "Obtained " << watchData.size() << " items:\n";
1682 foreach (const WatchData &wd, watchData)
1683 nsp << wd.toString() <<'\n';
1685 const bool forNewStackFrame = reply->cookie.toBool();
1686 if (forNewStackFrame)
1687 emit stackFrameCompleted();
1689 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1693 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1695 if (!reply->success)
1696 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1699 enum CdbExecutionStatus {
1700 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1701 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1702 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1703 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1704 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1705 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1706 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1708 static const char *cdbStatusName(unsigned long s)
1711 case CDB_STATUS_NO_CHANGE:
1715 case CDB_STATUS_GO_HANDLED:
1716 return "go_handled";
1717 case CDB_STATUS_GO_NOT_HANDLED:
1718 return "go_not_handled";
1719 case CDB_STATUS_STEP_OVER:
1721 case CDB_STATUS_STEP_INTO:
1723 case CDB_STATUS_BREAK:
1725 case CDB_STATUS_NO_DEBUGGEE:
1726 return "no_debuggee";
1727 case CDB_STATUS_STEP_BRANCH:
1728 return "step_branch";
1729 case CDB_STATUS_IGNORE_EVENT:
1730 return "ignore_event";
1731 case CDB_STATUS_RESTART_REQUESTED:
1732 return "restart_requested";
1733 case CDB_STATUS_REVERSE_GO:
1734 return "reverse_go";
1735 case CDB_STATUS_REVERSE_STEP_BRANCH:
1736 return "reverse_step_branch";
1737 case CDB_STATUS_REVERSE_STEP_OVER:
1738 return "reverse_step_over";
1739 case CDB_STATUS_REVERSE_STEP_INTO:
1740 return "reverse_step_into";
1745 /* Examine how to react to a stop. */
1746 enum StopActionFlags
1749 StopReportLog = 0x1,
1750 StopReportStatusMessage = 0x2,
1751 StopReportParseError = 0x4,
1752 StopShowExceptionMessageBox = 0x8,
1753 // Notify stop or just continue
1754 StopNotifyStop = 0x10,
1755 StopIgnoreContinue = 0x20,
1756 // Hit on break in artificial stop thread (created by DebugBreak()).
1757 StopInArtificialThread = 0x40,
1758 StopShutdownInProgress = 0x80 // Shutdown in progress
1761 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1763 QString *exceptionBoxMessage)
1765 // Report stop reason (GDBMI)
1767 if (targetState() == DebuggerFinished)
1768 rc |= StopShutdownInProgress;
1770 qDebug("%s", stopReason.toString(true, 4).constData());
1771 const QByteArray reason = stopReason.findChild("reason").data();
1772 if (reason.isEmpty()) {
1773 *message = tr("Malformed stop response received.");
1774 rc |= StopReportParseError|StopNotifyStop;
1777 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1778 if (state() == InferiorStopOk) {
1779 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1780 arg(QString::fromAscii(reason));
1781 rc |= StopReportLog;
1784 const int threadId = stopReason.findChild("threadId").data().toInt();
1785 if (reason == "breakpoint") {
1786 // Note: Internal breakpoints (run to line) are reported with id=0.
1787 // Step out creates temporary breakpoints with id 10000.
1788 BreakpointId id = 0;
1790 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1791 if (breakpointIdG.isValid()) {
1792 id = breakpointIdG.data().toULongLong();
1793 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1794 number = breakHandler()->response(id).number;
1799 if (id && breakHandler()->type(id) == Watchpoint) {
1800 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1802 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1804 rc |= StopReportStatusMessage|StopNotifyStop;
1807 if (reason == "exception") {
1808 WinException exception;
1809 exception.fromGdbMI(stopReason);
1810 QString description = exception.toString();
1812 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1813 // pulls DLLs. Avoid showing a 'stopped' Message box.
1814 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1815 || exception.exceptionCode == winExceptionWX86Breakpoint)
1816 return StopNotifyStop;
1817 if (exception.exceptionCode == winExceptionCtrlPressed) {
1818 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1819 *message = msgInterrupted();
1820 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1823 if (isDebuggerWinException(exception.exceptionCode)) {
1824 rc |= StopReportStatusMessage|StopNotifyStop;
1825 // Detect interruption by DebugBreak() and force a switch to thread 0.
1826 if (exception.function == "ntdll!DbgBreakPoint")
1827 rc |= StopInArtificialThread;
1828 *message = msgInterrupted();
1832 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1833 *message = description;
1834 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1837 *message = msgStopped(QLatin1String(reason));
1838 rc |= StopReportStatusMessage|StopNotifyStop;
1842 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1848 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1849 elapsedLogTime(), messageBA.constData(),
1850 stateName(state()), m_specialStopMode);
1852 // Switch source level debugging
1853 syncOperateByInstruction(m_operateByInstructionPending);
1855 // Engine-special stop reasons: Breakpoints and setup
1856 const SpecialStopMode specialStopMode = m_specialStopMode;
1858 m_specialStopMode = NoSpecialStop;
1860 switch(specialStopMode) {
1861 case SpecialStopSynchronizeBreakpoints:
1863 qDebug("attemptBreakpointSynchronization in special stop");
1864 attemptBreakpointSynchronization();
1865 doContinueInferior();
1867 case SpecialStopGetWidgetAt:
1868 postWidgetAtCommand();
1870 case CustomSpecialStop:
1871 foreach (const QVariant &data, m_customSpecialStopData)
1872 handleCustomSpecialStop(data);
1873 m_customSpecialStopData.clear();
1874 doContinueInferior();
1880 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1881 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1882 notifyEngineSetupOk();
1886 // Further examine stop and report to user
1888 QString exceptionBoxMessage;
1890 stopReason.fromString(messageBA);
1891 int forcedThreadId = -1;
1892 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage);
1893 // Do the non-blocking log reporting
1894 if (stopFlags & StopReportLog)
1895 showMessage(message, LogMisc);
1896 if (stopFlags & StopReportStatusMessage)
1897 showStatusMessage(message);
1898 if (stopFlags & StopReportParseError)
1899 showMessage(message, LogError);
1900 // Ignore things like WOW64
1901 if (stopFlags & StopIgnoreContinue) {
1902 postCommand("g", 0);
1905 // Notify about state and send off command sequence to get stack, etc.
1906 if (stopFlags & StopNotifyStop) {
1907 if (state() == InferiorStopRequested) {
1908 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1909 notifyInferiorStopOk();
1911 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1912 notifyInferiorSpontaneousStop();
1914 // Prevent further commands from being sent if shutdown is in progress
1915 if (stopFlags & StopShutdownInProgress) {
1916 showMessage(QString::fromLatin1("Shutdown request detected..."));
1919 const bool sourceStepInto = m_sourceStepInto;
1920 m_sourceStepInto = false;
1921 // Start sequence to get all relevant data.
1922 if (stopFlags & StopInArtificialThread) {
1923 showMessage(tr("Switching to main thread..."), LogMisc);
1924 postCommand("~0 s", 0);
1926 // Re-fetch stack again.
1927 postCommandSequence(CommandListStack);
1929 const GdbMi stack = stopReason.findChild("stack");
1930 if (stack.isValid()) {
1931 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1932 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1936 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1939 const GdbMi threads = stopReason.findChild("threads");
1940 if (threads.isValid()) {
1941 parseThreads(threads, forcedThreadId);
1943 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1945 // Fire off remaining commands asynchronously
1946 if (!m_pendingBreakpointMap.isEmpty())
1947 postCommandSequence(CommandListBreakPoints);
1948 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1949 postCommandSequence(CommandListRegisters);
1950 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
1951 postCommandSequence(CommandListModules);
1953 // After the sequence has been sent off and CDB is pondering the commands,
1954 // pop up a message box for exceptions.
1955 if (stopFlags & StopShowExceptionMessageBox)
1956 showStoppedByExceptionMessageBox(exceptionBoxMessage);
1959 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
1961 const DebuggerState s = state();
1962 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
1966 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
1967 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1970 case EngineShutdownRequested:
1973 case InferiorShutdownRequested:
1981 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
1983 const DebuggerState s = state();
1986 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
1990 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
1991 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1994 case EngineSetupRequested:
1996 case EngineRunRequested:
1997 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
1998 notifyEngineRunAndInferiorRunOk();
2001 case InferiorStopOk:
2002 // Inaccessible without debuggee (exit breakpoint)
2003 // We go for spontaneous engine shutdown instead.
2004 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2006 qDebug("Lost debuggeee");
2007 m_hasDebuggee = false;
2010 case InferiorRunRequested:
2011 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2012 notifyInferiorRunOk();
2015 case EngineShutdownRequested:
2022 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2025 QDebug nospace = qDebug().nospace();
2026 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2027 << ' ' << stateName(state());
2028 if (t == 'N' || debug > 1) {
2029 nospace << ' ' << message;
2031 nospace << ' ' << message.size() << " bytes";
2035 // Is there a reply expected, some command queued?
2036 if (t == 'R' || t == 'N') {
2037 if (token == -1) { // Default token, user typed in extension command
2038 showMessage(QString::fromLatin1(message), LogMisc);
2041 const int index = indexOfCommand(m_extensionCommandQueue, token);
2043 // Did the command finish? Take off queue and complete, invoke CB
2044 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2046 command->success = true;
2047 command->reply = message;
2049 command->success = false;
2050 command->errorMessage = message;
2053 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2054 command->command.constData(), command->token, m_extensionCommandQueue.size());
2055 if (command->handler)
2056 (this->*(command->handler))(command);
2061 if (what == "debuggee_output") {
2062 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2066 if (what == "event") {
2067 showStatusMessage(QString::fromAscii(message), 5000);
2071 if (what == "session_accessible") {
2072 if (!m_accessible) {
2073 m_accessible = true;
2074 handleSessionAccessible(message.toULong());
2079 if (what == "session_inaccessible") {
2081 m_accessible = false;
2082 handleSessionInaccessible(message.toULong());
2087 if (what == "session_idle") {
2088 handleSessionIdle(message);
2092 if (what == "exception") {
2093 WinException exception;
2095 gdbmi.fromString(message);
2096 exception.fromGdbMI(gdbmi);
2097 const QString message = exception.toString(true);
2098 showStatusMessage(message);
2099 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2100 if (exception.exceptionCode == winExceptionCppException)
2101 showMessage(message + QLatin1Char('\n'), AppOutput);
2109 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2110 enum { CdbPromptLength = 7 };
2112 static inline bool isCdbPrompt(const QByteArray &c)
2114 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2115 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2116 && std::isdigit(c.at(4));
2119 // Check for '<token>32>' or '<token>32<'
2120 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2121 int *token, bool *isStart)
2125 const int tokenPrefixSize = tokenPrefix.size();
2126 const int size = c.size();
2127 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2129 switch (c.at(size - 1)) {
2139 if (!c.startsWith(tokenPrefix))
2142 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2146 void CdbEngine::parseOutputLine(QByteArray line)
2148 // The hooked output callback in the extension suppresses prompts,
2149 // it should happen only in initial and exit stages. Note however that
2150 // if the output is not hooked, sequences of prompts are possible which
2151 // can mix things up.
2152 while (isCdbPrompt(line))
2153 line.remove(0, CdbPromptLength);
2154 // An extension notification (potentially consisting of several chunks)
2155 if (line.startsWith(m_creatorExtPrefix)) {
2156 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2157 const char type = line.at(m_creatorExtPrefix.size());
2159 const int tokenPos = m_creatorExtPrefix.size() + 2;
2160 const int tokenEndPos = line.indexOf('|', tokenPos);
2161 QTC_ASSERT(tokenEndPos != -1, return)
2162 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2164 const int remainingChunksPos = tokenEndPos + 1;
2165 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2166 QTC_ASSERT(remainingChunksEndPos != -1, return)
2167 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2168 // const char 'serviceName'
2169 const int whatPos = remainingChunksEndPos + 1;
2170 const int whatEndPos = line.indexOf('|', whatPos);
2171 QTC_ASSERT(whatEndPos != -1, return)
2172 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2173 // Build up buffer, call handler once last chunk was encountered
2174 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2175 if (remainingChunks == 0) {
2176 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2177 m_extensionMessageBuffer.clear();
2181 // Check for command start/end tokens within which the builtin command
2182 // output is enclosed
2184 bool isStartToken = false;
2185 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2187 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2188 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2190 // If there is a current command, wait for end of output indicated by token,
2191 // command, trigger handler and finish, else append to its output.
2192 if (m_currentBuiltinCommandIndex != -1) {
2193 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2194 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2195 if (isCommandToken) {
2196 // Did the command finish? Invoke callback and remove from queue.
2198 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2199 currentCommand->command.constData(), currentCommand->token,
2200 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2201 QTC_ASSERT(token == currentCommand->token, return; );
2202 if (currentCommand->handler)
2203 (this->*(currentCommand->handler))(currentCommand);
2204 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2205 m_currentBuiltinCommandIndex = -1;
2207 // Record output of current command
2208 currentCommand->reply.push_back(line);
2211 } // m_currentCommandIndex
2212 if (isCommandToken) {
2213 // Beginning command token encountered, start to record output.
2214 const int index = indexOfCommand(m_builtinCommandQueue, token);
2215 QTC_ASSERT(isStartToken && index != -1, return; );
2216 m_currentBuiltinCommandIndex = index;
2217 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2219 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2223 showMessage(QString::fromLocal8Bit(line), LogMisc);
2226 void CdbEngine::readyReadStandardOut()
2228 if (m_ignoreCdbOutput)
2230 m_outputBuffer += m_process.readAllStandardOutput();
2231 // Split into lines and parse line by line.
2233 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2234 if (endOfLinePos == -1) {
2238 QByteArray line = m_outputBuffer.left(endOfLinePos);
2239 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2240 line.truncate(line.size() - 1);
2241 parseOutputLine(line);
2242 m_outputBuffer.remove(0, endOfLinePos + 1);
2247 void CdbEngine::readyReadStandardError()
2249 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2252 void CdbEngine::processError()
2254 showMessage(m_process.errorString(), LogError);
2258 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2259 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2261 QByteArray cmd(cmdC);
2262 ByteArrayInputStream str(cmd);
2263 foreach(const BreakpointData *bp, bps)
2264 str << ' ' << bp->bpNumber;
2269 bool CdbEngine::stateAcceptsBreakpointChanges() const
2273 case InferiorStopOk:
2281 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2283 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2284 if (!DebuggerEngine::isCppBreakpoint(data))
2286 switch (data.type) {
2288 case BreakpointAtFork:
2289 //case BreakpointAtVFork:
2290 case BreakpointAtSysCall:
2293 case BreakpointByFileAndLine:
2294 case BreakpointByFunction:
2295 case BreakpointByAddress:
2296 case BreakpointAtThrow:
2297 case BreakpointAtCatch:
2298 case BreakpointAtMain:
2299 case BreakpointAtExec:
2305 void CdbEngine::attemptBreakpointSynchronization()
2308 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2309 // Check if there is anything to be done at all.
2310 BreakHandler *handler = breakHandler();
2311 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2312 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2313 if (acceptsBreakpoint(id))
2314 handler->setEngine(id, this);
2316 // Quick check: is there a need to change something? - Populate module cache
2317 bool changed = false;
2318 const BreakpointIds ids = handler->engineBreakpointIds(this);
2319 foreach (BreakpointId id, ids) {
2320 switch (handler->state(id)) {
2321 case BreakpointInsertRequested:
2322 case BreakpointRemoveRequested:
2323 case BreakpointChangeRequested:
2326 case BreakpointInserted: {
2327 // Collect the new modules matching the files.
2328 // In the future, that information should be obtained from the build system.
2329 const BreakpointParameters &data = handler->breakpointData(id);
2330 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2331 m_fileNameModuleHash.insert(data.fileName, data.module);
2339 if (debugBreakpoints)
2340 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2341 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2345 if (!m_accessible) {
2347 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2348 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2351 // Add/Change breakpoints and store pending ones in map, since
2352 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2353 // handleBreakPoints will the complete that information and set it on the break handler.
2354 bool addedChanged = false;
2355 foreach (BreakpointId id, ids) {
2356 BreakpointParameters parameters = handler->breakpointData(id);
2357 BreakpointResponse response;
2358 response.fromParameters(parameters);
2359 // If we encountered that file and have a module for it: Add it.
2360 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2361 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2362 if (it != m_fileNameModuleHash.constEnd())
2363 parameters.module = it.value();
2365 switch (handler->state(id)) {
2366 case BreakpointInsertRequested:
2367 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2368 if (!parameters.enabled)
2369 postCommand("bd " + QByteArray::number(id), 0);
2370 handler->notifyBreakpointInsertProceeding(id);
2371 handler->notifyBreakpointInsertOk(id);
2372 m_pendingBreakpointMap.insert(id, response);
2373 addedChanged = true;
2374 // Ensure enabled/disabled is correct in handler and line number is there.
2375 handler->setResponse(id, response);
2376 if (debugBreakpoints)
2377 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2379 case BreakpointChangeRequested:
2380 handler->notifyBreakpointChangeProceeding(id);
2381 if (debugBreakpoints)
2382 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2383 qPrintable(parameters.toString()));
2384 if (parameters.enabled != handler->response(id).enabled) {
2385 // Change enabled/disabled breakpoints without triggering update.
2386 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2387 response.pending = false;
2388 response.enabled = parameters.enabled;
2389 handler->setResponse(id, response);
2391 // Delete and re-add, triggering update
2392 addedChanged = true;
2393 postCommand("bc " + QByteArray::number(id), 0);
2394 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2395 m_pendingBreakpointMap.insert(id, response);
2397 handler->notifyBreakpointChangeOk(id);
2399 case BreakpointRemoveRequested:
2400 postCommand("bc " + QByteArray::number(id), 0);
2401 handler->notifyBreakpointRemoveProceeding(id);
2402 handler->notifyBreakpointRemoveOk(id);
2403 m_pendingBreakpointMap.remove(id);
2409 // List breakpoints and send responses
2411 postCommandSequence(CommandListBreakPoints);
2414 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2415 // manager to correctly process it) and convert to clean path.
2416 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2419 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2420 if (it != m_normalizedFileCache.constEnd())
2422 if (debugSourceMapping)
2423 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2424 // Do we have source path mappings? ->Apply.
2425 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2427 // Up/lower case normalization according to Windows.
2429 QString normalized = winNormalizeFileName(fileName);
2431 QString normalized = fileName;
2433 if (debugSourceMapping)
2434 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2435 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2436 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2437 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2439 // At least upper case drive letter if failed.
2440 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2441 result.fileName[0] = result.fileName.at(0).toUpper();
2443 m_normalizedFileCache.insert(f, result);
2444 if (debugSourceMapping)
2445 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2449 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2450 // has more processing.
2451 static StackFrames parseFrames(const GdbMi &gdbmi)
2454 const int count = gdbmi.childCount();
2456 for (int i = 0; i < count; i++) {
2457 const GdbMi &frameMi = gdbmi.childAt(i);
2460 const GdbMi fullName = frameMi.findChild("fullname");
2461 if (fullName.isValid()) {
2462 frame.file = QFile::decodeName(fullName.data());
2463 frame.line = frameMi.findChild("line").data().toInt();
2464 frame.usable = false; // To be decided after source path mapping.
2466 frame.function = QLatin1String(frameMi.findChild("func").data());
2467 frame.from = QLatin1String(frameMi.findChild("from").data());
2468 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2469 rc.push_back(frame);
2474 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2476 // Parse frames, find current. Special handling for step into:
2477 // When stepping into on an actual function (source mode) by executing 't', an assembler
2478 // frame pointing at the jmp instruction is hit (noticeable by top function being
2479 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2480 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2482 StackFrames frames = parseFrames(data);
2483 const int count = frames.size();
2484 for (int i = 0; i < count; i++) {
2485 const bool hasFile = !frames.at(i).file.isEmpty();
2486 // jmp-frame hit by step into, do another 't' and abort sequence.
2487 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2488 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2489 return ParseStackStepInto;
2492 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2493 frames[i].file = fileName.fileName;
2494 frames[i].usable = fileName.exists;
2495 if (current == -1 && frames[i].usable)
2499 if (count && current == -1) // No usable frame, use assembly.
2502 stackHandler()->setFrames(frames);
2503 activateFrame(current);
2507 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2509 if (command->success) {
2511 data.fromString(command->reply);
2512 parseStackTrace(data, false);
2513 postCommandSequence(command->commandSequence);
2515 showMessage(command->errorMessage, LogError);
2519 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2521 postCommandSequence(command->commandSequence);
2524 // Post a sequence of standard commands: Trigger next once one completes successfully
2525 void CdbEngine::postCommandSequence(unsigned mask)
2528 qDebug("postCommandSequence 0x%x\n", mask);
2532 if (mask & CommandListThreads) {
2533 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2536 if (mask & CommandListStack) {
2537 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2540 if (mask & CommandListRegisters) {
2541 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2542 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2545 if (mask & CommandListModules) {
2546 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2549 if (mask & CommandListBreakPoints) {
2550 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2551 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2556 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2558 bool success = false;
2561 if (!reply->success) {
2562 message = QString::fromAscii(reply->errorMessage);
2565 // Should be "namespace::QWidget:0x555"
2566 QString watchExp = QString::fromAscii(reply->reply);
2567 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2569 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2572 // 0x000 -> nothing found
2573 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2574 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2577 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2578 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2579 watchExp.insert(0, QLatin1String("*("));
2580 watchHandler()->watchExpression(watchExp);
2584 showMessage(message, LogWarning);
2585 m_watchPointX = m_watchPointY = 0;
2588 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2591 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2595 str.setIntegerBase(16);
2596 str << ", at 0x" << r.address;
2597 str.setIntegerBase(10);
2600 str << ", disabled";
2601 if (!r.module.isEmpty())
2602 str << ", module: '" << r.module << '\'';
2606 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2608 if (debugBreakpoints)
2609 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2610 if (!reply->success) {
2611 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2615 value.fromString(reply->reply);
2616 if (value.type() != GdbMi::List) {
2617 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2620 handleBreakPoints(value);
2623 void CdbEngine::handleBreakPoints(const GdbMi &value)
2625 // Report all obtained parameters back. Note that not all parameters are reported
2626 // back, so, match by id and complete
2627 if (debugBreakpoints)
2628 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2630 QTextStream str(&message);
2631 BreakHandler *handler = breakHandler();
2632 foreach (const GdbMi &breakPointG, value.children()) {
2633 BreakpointResponse reportedResponse;
2634 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2635 if (debugBreakpoints)
2636 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2637 qPrintable(reportedResponse.toString()));
2639 if (!reportedResponse.pending) {
2640 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2641 if (it != m_pendingBreakpointMap.end()) {
2642 // Complete the response and set on handler.
2643 BreakpointResponse ¤tResponse = it.value();
2644 currentResponse.number = reportedResponse.number;
2645 currentResponse.address = reportedResponse.address;
2646 currentResponse.module = reportedResponse.module;
2647 currentResponse.pending = reportedResponse.pending;
2648 currentResponse.enabled = reportedResponse.enabled;
2649 formatCdbBreakPointResponse(id, currentResponse, str);
2650 if (debugBreakpoints)
2651 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2652 handler->setResponse(id, currentResponse);
2653 m_pendingBreakpointMap.erase(it);
2655 } // not pending reported
2657 if (m_pendingBreakpointMap.empty()) {
2658 str << QLatin1String("All breakpoints have been resolved.\n");
2660 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2662 showMessage(message, LogMisc);
2665 void CdbEngine::watchPoint(const QPoint &p)
2667 m_watchPointX = p.x();
2668 m_watchPointY = p.y();
2670 case InferiorStopOk:
2671 postWidgetAtCommand();
2674 // "Select Widget to Watch" from a running application is currently not
2675 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2676 // but requires some work as not to confuse the engine by state-change notifications
2677 // emitted by the debuggee function call.
2678 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2681 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2682 arg(QString::fromAscii(stateName(state()))), LogWarning);
2687 void CdbEngine::postWidgetAtCommand()
2689 QByteArray arguments = QByteArray::number(m_watchPointX);
2690 arguments.append(' ');
2691 arguments.append(QByteArray::number(m_watchPointY));
2692 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2695 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2697 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2698 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2699 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2704 } // namespace Internal
2705 } // namespace Debugger