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
1436 if (debuggerCore()->boolSetting(UseCodeModel)) {
1437 QStringList uninitializedVariables;
1438 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1439 frame.function, frame.file, frame.line, &uninitializedVariables);
1440 if (!uninitializedVariables.isEmpty()) {
1441 str << blankSeparator << "-u ";
1443 foreach(const QString &u, uninitializedVariables) {
1446 str << localsPrefixC << u;
1450 // Perform watches synchronization
1451 str << blankSeparator << "-W";
1452 const WatcherHash watcherHash = WatchHandler::watcherNames();
1453 if (!watcherHash.isEmpty()) {
1454 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1455 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1456 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1460 // Required arguments: frame
1461 str << blankSeparator << frameIndex;
1462 watchHandler()->beginCycle();
1463 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1466 void CdbEngine::selectThread(int index)
1468 if (index < 0 || index == threadsHandler()->currentThread())
1472 const int newThreadId = threadsHandler()->threads().at(index).id;
1473 threadsHandler()->setCurrentThread(index);
1475 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1476 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1479 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1481 QTC_ASSERT(m_accessible, return;)
1483 ByteArrayInputStream str(cmd);
1484 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1485 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1486 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1489 // Parse: "00000000`77606060 cc int 3"
1490 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1492 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1493 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1494 DisassemblerLines disassemblerLines;
1495 foreach(const QByteArray &line, command->reply)
1496 disassemblerLines.appendLine(DisassemblerLine(QString::fromLatin1(line)));
1497 agent->setContents(disassemblerLines);
1500 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1502 if (!m_accessible) {
1503 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1507 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1510 ByteArrayInputStream str(args);
1511 str << addr << ' ' << length;
1512 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1513 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1516 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1518 QTC_ASSERT(!data.isEmpty(), return; )
1519 if (!m_accessible) {
1520 const MemoryChangeCookie cookie(addr, data);
1521 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1523 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1527 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1529 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1530 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1531 if (command->success) {
1532 const QByteArray data = QByteArray::fromBase64(command->reply);
1533 if (unsigned(data.size()) == memViewCookie.length)
1534 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1535 memViewCookie.address, data);
1537 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1541 void CdbEngine::reloadModules()
1543 postCommandSequence(CommandListModules);
1546 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1550 void CdbEngine::loadAllSymbols()
1554 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1556 Q_UNUSED(moduleName)
1559 void CdbEngine::reloadRegisters()
1561 postCommandSequence(CommandListRegisters);
1564 void CdbEngine::reloadSourceFiles()
1568 void CdbEngine::reloadFullStack()
1571 qDebug("%s", Q_FUNC_INFO);
1572 postCommandSequence(CommandListStack);
1575 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1577 if (reply->success) {
1578 m_inferiorPid = reply->reply.toUInt();
1579 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1580 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1581 notifyInferiorSetupOk();
1583 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1584 arg(QLatin1String(reply->errorMessage)), LogError);
1585 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1586 notifyInferiorSetupFailed();
1590 // Parse CDB gdbmi register syntax
1591 static inline Register parseRegister(const GdbMi &gdbmiReg)
1594 reg.name = gdbmiReg.findChild("name").data();
1595 const GdbMi description = gdbmiReg.findChild("description");
1596 if (description.type() != GdbMi::Invalid) {
1598 reg.name += description.data();
1601 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1605 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1607 if (reply->success) {
1609 value.fromString(reply->reply);
1610 if (value.type() == GdbMi::List) {
1612 modules.reserve(value.childCount());
1613 foreach (const GdbMi &gdbmiModule, value.children()) {
1615 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1616 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1617 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1618 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1619 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1620 module.symbolsRead = Module::ReadOk;
1621 modules.push_back(module);
1623 modulesHandler()->setModules(modules);
1625 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1626 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1629 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1630 arg(QLatin1String(reply->errorMessage)), LogError);
1632 postCommandSequence(reply->commandSequence);
1636 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1638 if (reply->success) {
1640 value.fromString(reply->reply);
1641 if (value.type() == GdbMi::List) {
1642 Registers registers;
1643 registers.reserve(value.childCount());
1644 foreach (const GdbMi &gdbmiReg, value.children())
1645 registers.push_back(parseRegister(gdbmiReg));
1646 registerHandler()->setRegisters(registers);
1648 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1649 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1652 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1653 arg(QLatin1String(reply->errorMessage)), LogError);
1655 postCommandSequence(reply->commandSequence);
1658 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1660 if (reply->success) {
1661 QList<WatchData> watchData;
1663 root.fromString(reply->reply);
1664 QTC_ASSERT(root.isList(), return ; )
1666 qDebug() << root.toString(true, 4);
1668 // Courtesy of GDB engine
1669 foreach (const GdbMi &child, root.children()) {
1671 dummy.iname = child.findChild("iname").data();
1672 dummy.name = QLatin1String(child.findChild("name").data());
1673 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1675 watchHandler()->insertBulkData(watchData);
1676 watchHandler()->endCycle();
1678 QDebug nsp = qDebug().nospace();
1679 nsp << "Obtained " << watchData.size() << " items:\n";
1680 foreach (const WatchData &wd, watchData)
1681 nsp << wd.toString() <<'\n';
1683 const bool forNewStackFrame = reply->cookie.toBool();
1684 if (forNewStackFrame)
1685 emit stackFrameCompleted();
1687 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1691 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1693 if (!reply->success)
1694 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1697 enum CdbExecutionStatus {
1698 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1699 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1700 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1701 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1702 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1703 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1704 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1706 static const char *cdbStatusName(unsigned long s)
1709 case CDB_STATUS_NO_CHANGE:
1713 case CDB_STATUS_GO_HANDLED:
1714 return "go_handled";
1715 case CDB_STATUS_GO_NOT_HANDLED:
1716 return "go_not_handled";
1717 case CDB_STATUS_STEP_OVER:
1719 case CDB_STATUS_STEP_INTO:
1721 case CDB_STATUS_BREAK:
1723 case CDB_STATUS_NO_DEBUGGEE:
1724 return "no_debuggee";
1725 case CDB_STATUS_STEP_BRANCH:
1726 return "step_branch";
1727 case CDB_STATUS_IGNORE_EVENT:
1728 return "ignore_event";
1729 case CDB_STATUS_RESTART_REQUESTED:
1730 return "restart_requested";
1731 case CDB_STATUS_REVERSE_GO:
1732 return "reverse_go";
1733 case CDB_STATUS_REVERSE_STEP_BRANCH:
1734 return "reverse_step_branch";
1735 case CDB_STATUS_REVERSE_STEP_OVER:
1736 return "reverse_step_over";
1737 case CDB_STATUS_REVERSE_STEP_INTO:
1738 return "reverse_step_into";
1743 /* Examine how to react to a stop. */
1744 enum StopActionFlags
1747 StopReportLog = 0x1,
1748 StopReportStatusMessage = 0x2,
1749 StopReportParseError = 0x4,
1750 StopShowExceptionMessageBox = 0x8,
1751 // Notify stop or just continue
1752 StopNotifyStop = 0x10,
1753 StopIgnoreContinue = 0x20,
1754 // Hit on break in artificial stop thread (created by DebugBreak()).
1755 StopInArtificialThread = 0x40,
1756 StopShutdownInProgress = 0x80 // Shutdown in progress
1759 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1761 QString *exceptionBoxMessage)
1763 // Report stop reason (GDBMI)
1765 if (targetState() == DebuggerFinished)
1766 rc |= StopShutdownInProgress;
1768 qDebug("%s", stopReason.toString(true, 4).constData());
1769 const QByteArray reason = stopReason.findChild("reason").data();
1770 if (reason.isEmpty()) {
1771 *message = tr("Malformed stop response received.");
1772 rc |= StopReportParseError|StopNotifyStop;
1775 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1776 if (state() == InferiorStopOk) {
1777 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1778 arg(QString::fromAscii(reason));
1779 rc |= StopReportLog;
1782 const int threadId = stopReason.findChild("threadId").data().toInt();
1783 if (reason == "breakpoint") {
1784 // Note: Internal breakpoints (run to line) are reported with id=0.
1785 // Step out creates temporary breakpoints with id 10000.
1786 BreakpointId id = 0;
1788 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1789 if (breakpointIdG.isValid()) {
1790 id = breakpointIdG.data().toULongLong();
1791 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1792 number = breakHandler()->response(id).number;
1797 if (id && breakHandler()->type(id) == Watchpoint) {
1798 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1800 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1802 rc |= StopReportStatusMessage|StopNotifyStop;
1805 if (reason == "exception") {
1806 WinException exception;
1807 exception.fromGdbMI(stopReason);
1808 QString description = exception.toString();
1810 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1811 // pulls DLLs. Avoid showing a 'stopped' Message box.
1812 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1813 || exception.exceptionCode == winExceptionWX86Breakpoint)
1814 return StopNotifyStop;
1815 if (exception.exceptionCode == winExceptionCtrlPressed) {
1816 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1817 *message = msgInterrupted();
1818 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1821 if (isDebuggerWinException(exception.exceptionCode)) {
1822 rc |= StopReportStatusMessage|StopNotifyStop;
1823 // Detect interruption by DebugBreak() and force a switch to thread 0.
1824 if (exception.function == "ntdll!DbgBreakPoint")
1825 rc |= StopInArtificialThread;
1826 *message = msgInterrupted();
1830 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1831 *message = description;
1832 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1835 *message = msgStopped(QLatin1String(reason));
1836 rc |= StopReportStatusMessage|StopNotifyStop;
1840 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1846 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1847 elapsedLogTime(), messageBA.constData(),
1848 stateName(state()), m_specialStopMode);
1850 // Switch source level debugging
1851 syncOperateByInstruction(m_operateByInstructionPending);
1853 // Engine-special stop reasons: Breakpoints and setup
1854 const SpecialStopMode specialStopMode = m_specialStopMode;
1856 m_specialStopMode = NoSpecialStop;
1858 switch(specialStopMode) {
1859 case SpecialStopSynchronizeBreakpoints:
1861 qDebug("attemptBreakpointSynchronization in special stop");
1862 attemptBreakpointSynchronization();
1863 doContinueInferior();
1865 case SpecialStopGetWidgetAt:
1866 postWidgetAtCommand();
1868 case CustomSpecialStop:
1869 foreach (const QVariant &data, m_customSpecialStopData)
1870 handleCustomSpecialStop(data);
1871 m_customSpecialStopData.clear();
1872 doContinueInferior();
1878 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1879 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1880 notifyEngineSetupOk();
1884 // Further examine stop and report to user
1886 QString exceptionBoxMessage;
1888 stopReason.fromString(messageBA);
1889 int forcedThreadId = -1;
1890 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage);
1891 // Do the non-blocking log reporting
1892 if (stopFlags & StopReportLog)
1893 showMessage(message, LogMisc);
1894 if (stopFlags & StopReportStatusMessage)
1895 showStatusMessage(message);
1896 if (stopFlags & StopReportParseError)
1897 showMessage(message, LogError);
1898 // Ignore things like WOW64
1899 if (stopFlags & StopIgnoreContinue) {
1900 postCommand("g", 0);
1903 // Notify about state and send off command sequence to get stack, etc.
1904 if (stopFlags & StopNotifyStop) {
1905 if (state() == InferiorStopRequested) {
1906 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1907 notifyInferiorStopOk();
1909 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1910 notifyInferiorSpontaneousStop();
1912 // Prevent further commands from being sent if shutdown is in progress
1913 if (stopFlags & StopShutdownInProgress) {
1914 showMessage(QString::fromLatin1("Shutdown request detected..."));
1917 const bool sourceStepInto = m_sourceStepInto;
1918 m_sourceStepInto = false;
1919 // Start sequence to get all relevant data.
1920 if (stopFlags & StopInArtificialThread) {
1921 showMessage(tr("Switching to main thread..."), LogMisc);
1922 postCommand("~0 s", 0);
1924 // Re-fetch stack again.
1925 postCommandSequence(CommandListStack);
1927 const GdbMi stack = stopReason.findChild("stack");
1928 if (stack.isValid()) {
1929 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1930 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1934 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1937 const GdbMi threads = stopReason.findChild("threads");
1938 if (threads.isValid()) {
1939 parseThreads(threads, forcedThreadId);
1941 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1943 // Fire off remaining commands asynchronously
1944 if (!m_pendingBreakpointMap.isEmpty())
1945 postCommandSequence(CommandListBreakPoints);
1946 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1947 postCommandSequence(CommandListRegisters);
1948 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
1949 postCommandSequence(CommandListModules);
1951 // After the sequence has been sent off and CDB is pondering the commands,
1952 // pop up a message box for exceptions.
1953 if (stopFlags & StopShowExceptionMessageBox)
1954 showStoppedByExceptionMessageBox(exceptionBoxMessage);
1957 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
1959 const DebuggerState s = state();
1960 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
1964 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
1965 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1968 case EngineShutdownRequested:
1971 case InferiorShutdownRequested:
1979 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
1981 const DebuggerState s = state();
1984 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
1988 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
1989 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1992 case EngineSetupRequested:
1994 case EngineRunRequested:
1995 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
1996 notifyEngineRunAndInferiorRunOk();
1999 case InferiorStopOk:
2000 // Inaccessible without debuggee (exit breakpoint)
2001 // We go for spontaneous engine shutdown instead.
2002 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2004 qDebug("Lost debuggeee");
2005 m_hasDebuggee = false;
2008 case InferiorRunRequested:
2009 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2010 notifyInferiorRunOk();
2013 case EngineShutdownRequested:
2020 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2023 QDebug nospace = qDebug().nospace();
2024 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2025 << ' ' << stateName(state());
2026 if (t == 'N' || debug > 1) {
2027 nospace << ' ' << message;
2029 nospace << ' ' << message.size() << " bytes";
2033 // Is there a reply expected, some command queued?
2034 if (t == 'R' || t == 'N') {
2035 if (token == -1) { // Default token, user typed in extension command
2036 showMessage(QString::fromLatin1(message), LogMisc);
2039 const int index = indexOfCommand(m_extensionCommandQueue, token);
2041 // Did the command finish? Take off queue and complete, invoke CB
2042 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2044 command->success = true;
2045 command->reply = message;
2047 command->success = false;
2048 command->errorMessage = message;
2051 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2052 command->command.constData(), command->token, m_extensionCommandQueue.size());
2053 if (command->handler)
2054 (this->*(command->handler))(command);
2059 if (what == "debuggee_output") {
2060 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2064 if (what == "event") {
2065 showStatusMessage(QString::fromAscii(message), 5000);
2069 if (what == "session_accessible") {
2070 if (!m_accessible) {
2071 m_accessible = true;
2072 handleSessionAccessible(message.toULong());
2077 if (what == "session_inaccessible") {
2079 m_accessible = false;
2080 handleSessionInaccessible(message.toULong());
2085 if (what == "session_idle") {
2086 handleSessionIdle(message);
2090 if (what == "exception") {
2091 WinException exception;
2093 gdbmi.fromString(message);
2094 exception.fromGdbMI(gdbmi);
2095 const QString message = exception.toString(true);
2096 showStatusMessage(message);
2097 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2098 if (exception.exceptionCode == winExceptionCppException)
2099 showMessage(message + QLatin1Char('\n'), AppOutput);
2107 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2108 enum { CdbPromptLength = 7 };
2110 static inline bool isCdbPrompt(const QByteArray &c)
2112 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2113 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2114 && std::isdigit(c.at(4));
2117 // Check for '<token>32>' or '<token>32<'
2118 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2119 int *token, bool *isStart)
2123 const int tokenPrefixSize = tokenPrefix.size();
2124 const int size = c.size();
2125 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2127 switch (c.at(size - 1)) {
2137 if (!c.startsWith(tokenPrefix))
2140 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2144 void CdbEngine::parseOutputLine(QByteArray line)
2146 // The hooked output callback in the extension suppresses prompts,
2147 // it should happen only in initial and exit stages. Note however that
2148 // if the output is not hooked, sequences of prompts are possible which
2149 // can mix things up.
2150 while (isCdbPrompt(line))
2151 line.remove(0, CdbPromptLength);
2152 // An extension notification (potentially consisting of several chunks)
2153 if (line.startsWith(m_creatorExtPrefix)) {
2154 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2155 const char type = line.at(m_creatorExtPrefix.size());
2157 const int tokenPos = m_creatorExtPrefix.size() + 2;
2158 const int tokenEndPos = line.indexOf('|', tokenPos);
2159 QTC_ASSERT(tokenEndPos != -1, return)
2160 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2162 const int remainingChunksPos = tokenEndPos + 1;
2163 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2164 QTC_ASSERT(remainingChunksEndPos != -1, return)
2165 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2166 // const char 'serviceName'
2167 const int whatPos = remainingChunksEndPos + 1;
2168 const int whatEndPos = line.indexOf('|', whatPos);
2169 QTC_ASSERT(whatEndPos != -1, return)
2170 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2171 // Build up buffer, call handler once last chunk was encountered
2172 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2173 if (remainingChunks == 0) {
2174 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2175 m_extensionMessageBuffer.clear();
2179 // Check for command start/end tokens within which the builtin command
2180 // output is enclosed
2182 bool isStartToken = false;
2183 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2185 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2186 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2188 // If there is a current command, wait for end of output indicated by token,
2189 // command, trigger handler and finish, else append to its output.
2190 if (m_currentBuiltinCommandIndex != -1) {
2191 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2192 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2193 if (isCommandToken) {
2194 // Did the command finish? Invoke callback and remove from queue.
2196 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2197 currentCommand->command.constData(), currentCommand->token,
2198 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2199 QTC_ASSERT(token == currentCommand->token, return; );
2200 if (currentCommand->handler)
2201 (this->*(currentCommand->handler))(currentCommand);
2202 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2203 m_currentBuiltinCommandIndex = -1;
2205 // Record output of current command
2206 currentCommand->reply.push_back(line);
2209 } // m_currentCommandIndex
2210 if (isCommandToken) {
2211 // Beginning command token encountered, start to record output.
2212 const int index = indexOfCommand(m_builtinCommandQueue, token);
2213 QTC_ASSERT(isStartToken && index != -1, return; );
2214 m_currentBuiltinCommandIndex = index;
2215 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2217 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2221 showMessage(QString::fromLocal8Bit(line), LogMisc);
2224 void CdbEngine::readyReadStandardOut()
2226 if (m_ignoreCdbOutput)
2228 m_outputBuffer += m_process.readAllStandardOutput();
2229 // Split into lines and parse line by line.
2231 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2232 if (endOfLinePos == -1) {
2236 QByteArray line = m_outputBuffer.left(endOfLinePos);
2237 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2238 line.truncate(line.size() - 1);
2239 parseOutputLine(line);
2240 m_outputBuffer.remove(0, endOfLinePos + 1);
2245 void CdbEngine::readyReadStandardError()
2247 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2250 void CdbEngine::processError()
2252 showMessage(m_process.errorString(), LogError);
2256 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2257 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2259 QByteArray cmd(cmdC);
2260 ByteArrayInputStream str(cmd);
2261 foreach(const BreakpointData *bp, bps)
2262 str << ' ' << bp->bpNumber;
2267 bool CdbEngine::stateAcceptsBreakpointChanges() const
2271 case InferiorStopOk:
2279 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2281 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2282 if (!DebuggerEngine::isCppBreakpoint(data))
2284 switch (data.type) {
2286 case BreakpointAtFork:
2287 //case BreakpointAtVFork:
2288 case BreakpointAtSysCall:
2291 case BreakpointByFileAndLine:
2292 case BreakpointByFunction:
2293 case BreakpointByAddress:
2294 case BreakpointAtThrow:
2295 case BreakpointAtCatch:
2296 case BreakpointAtMain:
2297 case BreakpointAtExec:
2303 void CdbEngine::attemptBreakpointSynchronization()
2306 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2307 // Check if there is anything to be done at all.
2308 BreakHandler *handler = breakHandler();
2309 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2310 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2311 if (acceptsBreakpoint(id))
2312 handler->setEngine(id, this);
2314 // Quick check: is there a need to change something? - Populate module cache
2315 bool changed = false;
2316 const BreakpointIds ids = handler->engineBreakpointIds(this);
2317 foreach (BreakpointId id, ids) {
2318 switch (handler->state(id)) {
2319 case BreakpointInsertRequested:
2320 case BreakpointRemoveRequested:
2321 case BreakpointChangeRequested:
2324 case BreakpointInserted: {
2325 // Collect the new modules matching the files.
2326 // In the future, that information should be obtained from the build system.
2327 const BreakpointParameters &data = handler->breakpointData(id);
2328 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2329 m_fileNameModuleHash.insert(data.fileName, data.module);
2337 if (debugBreakpoints)
2338 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2339 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2343 if (!m_accessible) {
2345 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2346 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2349 // Add/Change breakpoints and store pending ones in map, since
2350 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2351 // handleBreakPoints will the complete that information and set it on the break handler.
2352 bool addedChanged = false;
2353 foreach (BreakpointId id, ids) {
2354 BreakpointParameters parameters = handler->breakpointData(id);
2355 BreakpointResponse response;
2356 response.fromParameters(parameters);
2357 // If we encountered that file and have a module for it: Add it.
2358 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2359 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2360 if (it != m_fileNameModuleHash.constEnd())
2361 parameters.module = it.value();
2363 switch (handler->state(id)) {
2364 case BreakpointInsertRequested:
2365 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2366 if (!parameters.enabled)
2367 postCommand("bd " + QByteArray::number(id), 0);
2368 handler->notifyBreakpointInsertProceeding(id);
2369 handler->notifyBreakpointInsertOk(id);
2370 m_pendingBreakpointMap.insert(id, response);
2371 addedChanged = true;
2372 // Ensure enabled/disabled is correct in handler and line number is there.
2373 handler->setResponse(id, response);
2374 if (debugBreakpoints)
2375 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2377 case BreakpointChangeRequested:
2378 handler->notifyBreakpointChangeProceeding(id);
2379 if (debugBreakpoints)
2380 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2381 qPrintable(parameters.toString()));
2382 if (parameters.enabled != handler->response(id).enabled) {
2383 // Change enabled/disabled breakpoints without triggering update.
2384 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2385 response.pending = false;
2386 response.enabled = parameters.enabled;
2387 handler->setResponse(id, response);
2389 // Delete and re-add, triggering update
2390 addedChanged = true;
2391 postCommand("bc " + QByteArray::number(id), 0);
2392 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2393 m_pendingBreakpointMap.insert(id, response);
2395 handler->notifyBreakpointChangeOk(id);
2397 case BreakpointRemoveRequested:
2398 postCommand("bc " + QByteArray::number(id), 0);
2399 handler->notifyBreakpointRemoveProceeding(id);
2400 handler->notifyBreakpointRemoveOk(id);
2401 m_pendingBreakpointMap.remove(id);
2407 // List breakpoints and send responses
2409 postCommandSequence(CommandListBreakPoints);
2412 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2413 // manager to correctly process it) and convert to clean path.
2414 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2417 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2418 if (it != m_normalizedFileCache.constEnd())
2420 if (debugSourceMapping)
2421 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2422 // Do we have source path mappings? ->Apply.
2423 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2425 // Up/lower case normalization according to Windows.
2427 QString normalized = winNormalizeFileName(fileName);
2429 QString normalized = fileName;
2431 if (debugSourceMapping)
2432 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2433 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2434 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2435 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2437 // At least upper case drive letter if failed.
2438 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2439 result.fileName[0] = result.fileName.at(0).toUpper();
2441 m_normalizedFileCache.insert(f, result);
2442 if (debugSourceMapping)
2443 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2447 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2448 // has more processing.
2449 static StackFrames parseFrames(const GdbMi &gdbmi)
2452 const int count = gdbmi.childCount();
2454 for (int i = 0; i < count; i++) {
2455 const GdbMi &frameMi = gdbmi.childAt(i);
2458 const GdbMi fullName = frameMi.findChild("fullname");
2459 if (fullName.isValid()) {
2460 frame.file = QFile::decodeName(fullName.data());
2461 frame.line = frameMi.findChild("line").data().toInt();
2462 frame.usable = false; // To be decided after source path mapping.
2464 frame.function = QLatin1String(frameMi.findChild("func").data());
2465 frame.from = QLatin1String(frameMi.findChild("from").data());
2466 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2467 rc.push_back(frame);
2472 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2474 // Parse frames, find current. Special handling for step into:
2475 // When stepping into on an actual function (source mode) by executing 't', an assembler
2476 // frame pointing at the jmp instruction is hit (noticeable by top function being
2477 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2478 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2480 StackFrames frames = parseFrames(data);
2481 const int count = frames.size();
2482 for (int i = 0; i < count; i++) {
2483 const bool hasFile = !frames.at(i).file.isEmpty();
2484 // jmp-frame hit by step into, do another 't' and abort sequence.
2485 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2486 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2487 return ParseStackStepInto;
2490 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2491 frames[i].file = fileName.fileName;
2492 frames[i].usable = fileName.exists;
2493 if (current == -1 && frames[i].usable)
2497 if (count && current == -1) // No usable frame, use assembly.
2500 stackHandler()->setFrames(frames);
2501 activateFrame(current);
2505 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2507 if (command->success) {
2509 data.fromString(command->reply);
2510 parseStackTrace(data, false);
2511 postCommandSequence(command->commandSequence);
2513 showMessage(command->errorMessage, LogError);
2517 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2519 postCommandSequence(command->commandSequence);
2522 // Post a sequence of standard commands: Trigger next once one completes successfully
2523 void CdbEngine::postCommandSequence(unsigned mask)
2526 qDebug("postCommandSequence 0x%x\n", mask);
2530 if (mask & CommandListThreads) {
2531 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2534 if (mask & CommandListStack) {
2535 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2538 if (mask & CommandListRegisters) {
2539 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2540 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2543 if (mask & CommandListModules) {
2544 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2547 if (mask & CommandListBreakPoints) {
2548 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2549 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2554 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2556 bool success = false;
2559 if (!reply->success) {
2560 message = QString::fromAscii(reply->errorMessage);
2563 // Should be "namespace::QWidget:0x555"
2564 QString watchExp = QString::fromAscii(reply->reply);
2565 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2567 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2570 // 0x000 -> nothing found
2571 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2572 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2575 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2576 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2577 watchExp.insert(0, QLatin1String("*("));
2578 watchHandler()->watchExpression(watchExp);
2582 showMessage(message, LogWarning);
2583 m_watchPointX = m_watchPointY = 0;
2586 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2589 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2593 str.setIntegerBase(16);
2594 str << ", at 0x" << r.address;
2595 str.setIntegerBase(10);
2598 str << ", disabled";
2599 if (!r.module.isEmpty())
2600 str << ", module: '" << r.module << '\'';
2604 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2606 if (debugBreakpoints)
2607 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2608 if (!reply->success) {
2609 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2613 value.fromString(reply->reply);
2614 if (value.type() != GdbMi::List) {
2615 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2618 handleBreakPoints(value);
2621 void CdbEngine::handleBreakPoints(const GdbMi &value)
2623 // Report all obtained parameters back. Note that not all parameters are reported
2624 // back, so, match by id and complete
2625 if (debugBreakpoints)
2626 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2628 QTextStream str(&message);
2629 BreakHandler *handler = breakHandler();
2630 foreach (const GdbMi &breakPointG, value.children()) {
2631 BreakpointResponse reportedResponse;
2632 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2633 if (debugBreakpoints)
2634 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2635 qPrintable(reportedResponse.toString()));
2637 if (!reportedResponse.pending) {
2638 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2639 if (it != m_pendingBreakpointMap.end()) {
2640 // Complete the response and set on handler.
2641 BreakpointResponse ¤tResponse = it.value();
2642 currentResponse.number = reportedResponse.number;
2643 currentResponse.address = reportedResponse.address;
2644 currentResponse.module = reportedResponse.module;
2645 currentResponse.pending = reportedResponse.pending;
2646 currentResponse.enabled = reportedResponse.enabled;
2647 formatCdbBreakPointResponse(id, currentResponse, str);
2648 if (debugBreakpoints)
2649 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2650 handler->setResponse(id, currentResponse);
2651 m_pendingBreakpointMap.erase(it);
2653 } // not pending reported
2655 if (m_pendingBreakpointMap.empty()) {
2656 str << QLatin1String("All breakpoints have been resolved.\n");
2658 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2660 showMessage(message, LogMisc);
2663 void CdbEngine::watchPoint(const QPoint &p)
2665 m_watchPointX = p.x();
2666 m_watchPointY = p.y();
2668 case InferiorStopOk:
2669 postWidgetAtCommand();
2672 // "Select Widget to Watch" from a running application is currently not
2673 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2674 // but requires some work as not to confuse the engine by state-change notifications
2675 // emitted by the debuggee function call.
2676 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2679 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2680 arg(QString::fromAscii(stateName(state()))), LogWarning);
2685 void CdbEngine::postWidgetAtCommand()
2687 QByteArray arguments = QByteArray::number(m_watchPointX);
2688 arguments.append(' ');
2689 arguments.append(QByteArray::number(m_watchPointY));
2690 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2693 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2695 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2696 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2697 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2702 } // namespace Internal
2703 } // namespace Debugger