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 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1054 |BreakModuleCapability;
1057 void CdbEngine::executeStep()
1059 if (!m_operateByInstruction)
1060 m_sourceStepInto = true; // See explanation at handleStackTrace().
1061 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1062 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1063 notifyInferiorRunRequested();
1066 void CdbEngine::executeStepOut()
1068 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1069 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1070 notifyInferiorRunRequested();
1073 void CdbEngine::executeNext()
1075 postCommand(QByteArray("p"), 0); // Step over -> p
1076 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1077 notifyInferiorRunRequested();
1080 void CdbEngine::executeStepI()
1085 void CdbEngine::executeNextI()
1090 void CdbEngine::continueInferior()
1092 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1093 notifyInferiorRunRequested();
1094 doContinueInferior();
1097 void CdbEngine::doContinueInferior()
1099 postCommand(QByteArray("g"), 0);
1102 bool CdbEngine::canInterruptInferior() const
1104 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1107 void CdbEngine::interruptInferior()
1110 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1111 if (canInterruptInferior()) {
1112 doInterruptInferior(NoSpecialStop);
1114 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1115 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1116 notifyInferiorStopOk();
1117 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1118 notifyInferiorRunRequested();
1119 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1120 notifyInferiorRunOk();
1124 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1126 if (m_specialStopMode == NoSpecialStop)
1127 doInterruptInferior(CustomSpecialStop);
1128 m_customSpecialStopData.push_back(v);
1131 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1134 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1135 m_specialStopMode = sm;
1136 QString errorMessage;
1137 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1138 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1139 m_specialStopMode = oldSpecialMode;
1140 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1147 void CdbEngine::executeRunToLine(const ContextData &data)
1149 // Add one-shot breakpoint
1150 BreakpointParameters bp;
1152 bp.type =BreakpointByAddress;
1153 bp.address = data.address;
1155 bp.type =BreakpointByFileAndLine;
1156 bp.fileName = data.fileName;
1157 bp.lineNumber = data.lineNumber;
1159 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1163 void CdbEngine::executeRunToFunction(const QString &functionName)
1165 // Add one-shot breakpoint
1166 BreakpointParameters bp(BreakpointByFunction);
1167 bp.functionName = functionName;
1169 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1173 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1175 const Registers registers = registerHandler()->registers();
1176 QTC_ASSERT(regnr < registers.size(), return)
1177 // Value is decimal or 0x-hex-prefixed
1179 ByteArrayInputStream str(cmd);
1180 str << "r " << registers.at(regnr).name << '=' << value;
1181 postCommand(cmd, 0);
1185 void CdbEngine::executeJumpToLine(const ContextData &data)
1188 // Goto address directly.
1189 jumpToAddress(data.address);
1190 gotoLocation(Location(data.address));
1192 // Jump to source line: Resolve source line address and go to that location
1194 ByteArrayInputStream str(cmd);
1195 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1196 const QVariant cookie = qVariantFromValue(data);
1197 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1201 void CdbEngine::jumpToAddress(quint64 address)
1203 // Fake a jump to address by setting the PC register.
1204 QByteArray registerCmd;
1205 ByteArrayInputStream str(registerCmd);
1206 // PC-register depending on 64/32bit.
1207 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1208 str.setHexPrefix(true);
1209 str.setIntegerBase(16);
1211 postCommand(registerCmd, 0);
1214 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1216 if (cmd->reply.isEmpty())
1218 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1219 // Set register 'rip' to hex address and goto lcoation
1220 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1221 const int equalPos = answer.indexOf(" = ");
1224 answer.remove(0, equalPos + 3);
1225 answer.remove(QLatin1Char('`'));
1227 const quint64 address = answer.toLongLong(&ok, 16);
1228 if (ok && address) {
1229 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1230 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1231 jumpToAddress(address);
1232 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1236 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1239 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1241 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1242 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1247 ByteArrayInputStream str(cmd);
1248 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1249 postCommand(cmd, 0);
1250 // Update all locals in case we change a union or something pointed to
1251 // that affects other variables, too.
1255 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1257 int currentThreadId;
1258 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1259 threadsHandler()->setThreads(threads);
1260 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1261 forceCurrentThreadId : currentThreadId);
1264 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1267 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1268 if (reply->success) {
1270 data.fromString(reply->reply);
1272 // Continue sequence
1273 postCommandSequence(reply->commandSequence);
1275 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1279 void CdbEngine::executeDebuggerCommand(const QString &command)
1281 postCommand(command.toLocal8Bit(), QuietCommand);
1284 // Post command without callback
1285 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1288 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1289 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1290 if (!(flags & QuietCommand))
1291 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1292 m_process.write(cmd + '\n');
1295 // Post a built-in-command producing free-format output with a callback.
1296 // In order to catch the output, it is enclosed in 'echo' commands
1297 // printing a specially formatted token to be identifiable in the output.
1298 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1299 BuiltinCommandHandler handler,
1300 unsigned nextCommandFlag,
1301 const QVariant &cookie)
1303 if (!m_accessible) {
1304 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1305 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1306 showMessage(msg, LogError);
1309 if (!flags & QuietCommand)
1310 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1312 const int token = m_nextCommandToken++;
1313 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1315 m_builtinCommandQueue.push_back(pendingCommand);
1316 // Enclose command in echo-commands for token
1318 ByteArrayInputStream str(fullCmd);
1319 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1320 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1322 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1323 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1324 m_builtinCommandQueue.size(), nextCommandFlag);
1326 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1327 fullCmd.constData());
1328 m_process.write(fullCmd);
1331 // Post an extension command producing one-line output with a callback,
1332 // pass along token for identification in queue.
1333 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1334 const QByteArray &arguments,
1336 ExtensionCommandHandler handler,
1337 unsigned nextCommandFlag,
1338 const QVariant &cookie)
1340 if (!m_accessible) {
1341 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1342 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1343 showMessage(msg, LogError);
1347 const int token = m_nextCommandToken++;
1349 // Format full command with token to be recognizeable in the output
1351 ByteArrayInputStream str(fullCmd);
1352 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1353 if (!arguments.isEmpty())
1354 str << ' ' << arguments;
1356 if (!flags & QuietCommand)
1357 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1359 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1361 m_extensionCommandQueue.push_back(pendingCommand);
1362 // Enclose command in echo-commands for token
1364 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1365 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1366 m_extensionCommandQueue.size(), nextCommandFlag);
1367 m_process.write(fullCmd + '\n');
1370 void CdbEngine::activateFrame(int index)
1372 // TODO: assembler,etc
1375 const StackFrames &frames = stackHandler()->frames();
1376 QTC_ASSERT(index < frames.size(), return; )
1378 const StackFrame frame = frames.at(index);
1379 if (debug || debugLocals)
1380 qDebug("activateFrame idx=%d '%s' %d", index,
1381 qPrintable(frame.file), frame.line);
1382 stackHandler()->setCurrentIndex(index);
1383 const bool showAssembler = !frames.at(index).isUsable();
1384 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1385 watchHandler()->beginCycle();
1386 watchHandler()->endCycle();
1387 QAction *assemblerAction = theAssemblerAction();
1388 if (assemblerAction->isChecked()) {
1389 gotoLocation(frame);
1391 assemblerAction->trigger(); // Seems to trigger update
1394 gotoLocation(frame);
1399 void CdbEngine::updateLocals(bool forNewStackFrame)
1401 typedef QHash<QByteArray, int> WatcherHash;
1403 const int frameIndex = stackHandler()->currentIndex();
1404 if (frameIndex < 0) {
1405 watchHandler()->beginCycle();
1406 watchHandler()->endCycle();
1409 const StackFrame frame = stackHandler()->currentFrame();
1410 if (!frame.isUsable()) {
1411 watchHandler()->beginCycle();
1412 watchHandler()->endCycle();
1415 /* Watchers: Forcibly discard old symbol group as switching from
1416 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1417 * and cause errors as it seems to go 'stale' when switching threads.
1418 * Initial expand, get uninitialized and query */
1419 QByteArray arguments;
1420 ByteArrayInputStream str(arguments);
1423 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1424 if (!expanded.isEmpty()) {
1425 str << blankSeparator << "-e ";
1427 foreach(const QByteArray &e, expanded) {
1433 addLocalsOptions(str);
1434 // Uninitialized variables if desired
1435 if (debuggerCore()->boolSetting(UseCodeModel)) {
1436 QStringList uninitializedVariables;
1437 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1438 frame.function, frame.file, frame.line, &uninitializedVariables);
1439 if (!uninitializedVariables.isEmpty()) {
1440 str << blankSeparator << "-u ";
1442 foreach(const QString &u, uninitializedVariables) {
1445 str << localsPrefixC << u;
1449 // Perform watches synchronization
1450 str << blankSeparator << "-W";
1451 const WatcherHash watcherHash = WatchHandler::watcherNames();
1452 if (!watcherHash.isEmpty()) {
1453 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1454 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1455 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1459 // Required arguments: frame
1460 str << blankSeparator << frameIndex;
1461 watchHandler()->beginCycle();
1462 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1465 void CdbEngine::selectThread(int index)
1467 if (index < 0 || index == threadsHandler()->currentThread())
1471 const int newThreadId = threadsHandler()->threads().at(index).id;
1472 threadsHandler()->setCurrentThread(index);
1474 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1475 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1478 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1480 QTC_ASSERT(m_accessible, return;)
1482 ByteArrayInputStream str(cmd);
1483 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1484 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1485 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1488 // Parse: "00000000`77606060 cc int 3"
1489 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1491 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1492 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1493 DisassemblerLines disassemblerLines;
1494 foreach(const QByteArray &line, command->reply)
1495 disassemblerLines.appendLine(DisassemblerLine(QString::fromLatin1(line)));
1496 agent->setContents(disassemblerLines);
1499 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1501 if (!m_accessible) {
1502 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1506 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1509 ByteArrayInputStream str(args);
1510 str << addr << ' ' << length;
1511 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1512 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1515 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1517 QTC_ASSERT(!data.isEmpty(), return; )
1518 if (!m_accessible) {
1519 const MemoryChangeCookie cookie(addr, data);
1520 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1522 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1526 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1528 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1529 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1530 if (command->success) {
1531 const QByteArray data = QByteArray::fromBase64(command->reply);
1532 if (unsigned(data.size()) == memViewCookie.length)
1533 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1534 memViewCookie.address, data);
1536 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1540 void CdbEngine::reloadModules()
1542 postCommandSequence(CommandListModules);
1545 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1549 void CdbEngine::loadAllSymbols()
1553 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1555 Q_UNUSED(moduleName)
1558 void CdbEngine::reloadRegisters()
1560 postCommandSequence(CommandListRegisters);
1563 void CdbEngine::reloadSourceFiles()
1567 void CdbEngine::reloadFullStack()
1570 qDebug("%s", Q_FUNC_INFO);
1571 postCommandSequence(CommandListStack);
1574 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1576 if (reply->success) {
1577 m_inferiorPid = reply->reply.toUInt();
1578 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1579 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1580 notifyInferiorSetupOk();
1582 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1583 arg(QLatin1String(reply->errorMessage)), LogError);
1584 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1585 notifyInferiorSetupFailed();
1589 // Parse CDB gdbmi register syntax
1590 static inline Register parseRegister(const GdbMi &gdbmiReg)
1593 reg.name = gdbmiReg.findChild("name").data();
1594 const GdbMi description = gdbmiReg.findChild("description");
1595 if (description.type() != GdbMi::Invalid) {
1597 reg.name += description.data();
1600 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1604 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1606 if (reply->success) {
1608 value.fromString(reply->reply);
1609 if (value.type() == GdbMi::List) {
1611 modules.reserve(value.childCount());
1612 foreach (const GdbMi &gdbmiModule, value.children()) {
1614 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1615 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1616 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1617 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1618 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1619 module.symbolsRead = Module::ReadOk;
1620 modules.push_back(module);
1622 modulesHandler()->setModules(modules);
1624 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1625 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1628 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1629 arg(QLatin1String(reply->errorMessage)), LogError);
1631 postCommandSequence(reply->commandSequence);
1635 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1637 if (reply->success) {
1639 value.fromString(reply->reply);
1640 if (value.type() == GdbMi::List) {
1641 Registers registers;
1642 registers.reserve(value.childCount());
1643 foreach (const GdbMi &gdbmiReg, value.children())
1644 registers.push_back(parseRegister(gdbmiReg));
1645 registerHandler()->setRegisters(registers);
1647 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1648 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1651 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1652 arg(QLatin1String(reply->errorMessage)), LogError);
1654 postCommandSequence(reply->commandSequence);
1657 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1659 if (reply->success) {
1660 QList<WatchData> watchData;
1662 root.fromString(reply->reply);
1663 QTC_ASSERT(root.isList(), return ; )
1665 qDebug() << root.toString(true, 4);
1667 // Courtesy of GDB engine
1668 foreach (const GdbMi &child, root.children()) {
1670 dummy.iname = child.findChild("iname").data();
1671 dummy.name = QLatin1String(child.findChild("name").data());
1672 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1674 watchHandler()->insertBulkData(watchData);
1675 watchHandler()->endCycle();
1677 QDebug nsp = qDebug().nospace();
1678 nsp << "Obtained " << watchData.size() << " items:\n";
1679 foreach (const WatchData &wd, watchData)
1680 nsp << wd.toString() <<'\n';
1682 const bool forNewStackFrame = reply->cookie.toBool();
1683 if (forNewStackFrame)
1684 emit stackFrameCompleted();
1686 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1690 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1692 if (!reply->success)
1693 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1696 enum CdbExecutionStatus {
1697 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1698 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1699 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1700 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1701 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1702 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1703 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1705 static const char *cdbStatusName(unsigned long s)
1708 case CDB_STATUS_NO_CHANGE:
1712 case CDB_STATUS_GO_HANDLED:
1713 return "go_handled";
1714 case CDB_STATUS_GO_NOT_HANDLED:
1715 return "go_not_handled";
1716 case CDB_STATUS_STEP_OVER:
1718 case CDB_STATUS_STEP_INTO:
1720 case CDB_STATUS_BREAK:
1722 case CDB_STATUS_NO_DEBUGGEE:
1723 return "no_debuggee";
1724 case CDB_STATUS_STEP_BRANCH:
1725 return "step_branch";
1726 case CDB_STATUS_IGNORE_EVENT:
1727 return "ignore_event";
1728 case CDB_STATUS_RESTART_REQUESTED:
1729 return "restart_requested";
1730 case CDB_STATUS_REVERSE_GO:
1731 return "reverse_go";
1732 case CDB_STATUS_REVERSE_STEP_BRANCH:
1733 return "reverse_step_branch";
1734 case CDB_STATUS_REVERSE_STEP_OVER:
1735 return "reverse_step_over";
1736 case CDB_STATUS_REVERSE_STEP_INTO:
1737 return "reverse_step_into";
1742 /* Examine how to react to a stop. */
1743 enum StopActionFlags
1746 StopReportLog = 0x1,
1747 StopReportStatusMessage = 0x2,
1748 StopReportParseError = 0x4,
1749 StopShowExceptionMessageBox = 0x8,
1750 // Notify stop or just continue
1751 StopNotifyStop = 0x10,
1752 StopIgnoreContinue = 0x20,
1753 // Hit on break in artificial stop thread (created by DebugBreak()).
1754 StopInArtificialThread = 0x40,
1755 StopShutdownInProgress = 0x80 // Shutdown in progress
1758 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1760 QString *exceptionBoxMessage)
1762 // Report stop reason (GDBMI)
1764 if (targetState() == DebuggerFinished)
1765 rc |= StopShutdownInProgress;
1767 qDebug("%s", stopReason.toString(true, 4).constData());
1768 const QByteArray reason = stopReason.findChild("reason").data();
1769 if (reason.isEmpty()) {
1770 *message = tr("Malformed stop response received.");
1771 rc |= StopReportParseError|StopNotifyStop;
1774 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1775 if (state() == InferiorStopOk) {
1776 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1777 arg(QString::fromAscii(reason));
1778 rc |= StopReportLog;
1781 const int threadId = stopReason.findChild("threadId").data().toInt();
1782 if (reason == "breakpoint") {
1783 // Note: Internal breakpoints (run to line) are reported with id=0.
1784 // Step out creates temporary breakpoints with id 10000.
1785 BreakpointId id = 0;
1787 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1788 if (breakpointIdG.isValid()) {
1789 id = breakpointIdG.data().toULongLong();
1790 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1791 number = breakHandler()->response(id).number;
1796 if (id && breakHandler()->type(id) == Watchpoint) {
1797 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1799 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1801 rc |= StopReportStatusMessage|StopNotifyStop;
1804 if (reason == "exception") {
1805 WinException exception;
1806 exception.fromGdbMI(stopReason);
1807 QString description = exception.toString();
1809 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1810 // pulls DLLs. Avoid showing a 'stopped' Message box.
1811 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1812 || exception.exceptionCode == winExceptionWX86Breakpoint)
1813 return StopNotifyStop;
1814 if (exception.exceptionCode == winExceptionCtrlPressed) {
1815 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1816 *message = msgInterrupted();
1817 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1820 if (isDebuggerWinException(exception.exceptionCode)) {
1821 rc |= StopReportStatusMessage|StopNotifyStop;
1822 // Detect interruption by DebugBreak() and force a switch to thread 0.
1823 if (exception.function == "ntdll!DbgBreakPoint")
1824 rc |= StopInArtificialThread;
1825 *message = msgInterrupted();
1829 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1830 *message = description;
1831 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1834 *message = msgStopped(QLatin1String(reason));
1835 rc |= StopReportStatusMessage|StopNotifyStop;
1839 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1845 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1846 elapsedLogTime(), messageBA.constData(),
1847 stateName(state()), m_specialStopMode);
1849 // Switch source level debugging
1850 syncOperateByInstruction(m_operateByInstructionPending);
1852 // Engine-special stop reasons: Breakpoints and setup
1853 const SpecialStopMode specialStopMode = m_specialStopMode;
1855 m_specialStopMode = NoSpecialStop;
1857 switch(specialStopMode) {
1858 case SpecialStopSynchronizeBreakpoints:
1860 qDebug("attemptBreakpointSynchronization in special stop");
1861 attemptBreakpointSynchronization();
1862 doContinueInferior();
1864 case SpecialStopGetWidgetAt:
1865 postWidgetAtCommand();
1867 case CustomSpecialStop:
1868 foreach (const QVariant &data, m_customSpecialStopData)
1869 handleCustomSpecialStop(data);
1870 m_customSpecialStopData.clear();
1871 doContinueInferior();
1877 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1878 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1879 notifyEngineSetupOk();
1883 // Further examine stop and report to user
1885 QString exceptionBoxMessage;
1887 stopReason.fromString(messageBA);
1888 int forcedThreadId = -1;
1889 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage);
1890 // Do the non-blocking log reporting
1891 if (stopFlags & StopReportLog)
1892 showMessage(message, LogMisc);
1893 if (stopFlags & StopReportStatusMessage)
1894 showStatusMessage(message);
1895 if (stopFlags & StopReportParseError)
1896 showMessage(message, LogError);
1897 // Ignore things like WOW64
1898 if (stopFlags & StopIgnoreContinue) {
1899 postCommand("g", 0);
1902 // Notify about state and send off command sequence to get stack, etc.
1903 if (stopFlags & StopNotifyStop) {
1904 if (state() == InferiorStopRequested) {
1905 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1906 notifyInferiorStopOk();
1908 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1909 notifyInferiorSpontaneousStop();
1911 // Prevent further commands from being sent if shutdown is in progress
1912 if (stopFlags & StopShutdownInProgress) {
1913 showMessage(QString::fromLatin1("Shutdown request detected..."));
1916 const bool sourceStepInto = m_sourceStepInto;
1917 m_sourceStepInto = false;
1918 // Start sequence to get all relevant data.
1919 if (stopFlags & StopInArtificialThread) {
1920 showMessage(tr("Switching to main thread..."), LogMisc);
1921 postCommand("~0 s", 0);
1923 // Re-fetch stack again.
1924 postCommandSequence(CommandListStack);
1926 const GdbMi stack = stopReason.findChild("stack");
1927 if (stack.isValid()) {
1928 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1929 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1933 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1936 const GdbMi threads = stopReason.findChild("threads");
1937 if (threads.isValid()) {
1938 parseThreads(threads, forcedThreadId);
1940 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1942 // Fire off remaining commands asynchronously
1943 if (!m_pendingBreakpointMap.isEmpty())
1944 postCommandSequence(CommandListBreakPoints);
1945 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1946 postCommandSequence(CommandListRegisters);
1947 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
1948 postCommandSequence(CommandListModules);
1950 // After the sequence has been sent off and CDB is pondering the commands,
1951 // pop up a message box for exceptions.
1952 if (stopFlags & StopShowExceptionMessageBox)
1953 showStoppedByExceptionMessageBox(exceptionBoxMessage);
1956 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
1958 const DebuggerState s = state();
1959 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
1963 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
1964 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1967 case EngineShutdownRequested:
1970 case InferiorShutdownRequested:
1978 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
1980 const DebuggerState s = state();
1983 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
1987 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
1988 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1991 case EngineSetupRequested:
1993 case EngineRunRequested:
1994 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
1995 notifyEngineRunAndInferiorRunOk();
1998 case InferiorStopOk:
1999 // Inaccessible without debuggee (exit breakpoint)
2000 // We go for spontaneous engine shutdown instead.
2001 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2003 qDebug("Lost debuggeee");
2004 m_hasDebuggee = false;
2007 case InferiorRunRequested:
2008 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2009 notifyInferiorRunOk();
2012 case EngineShutdownRequested:
2019 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2022 QDebug nospace = qDebug().nospace();
2023 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2024 << ' ' << stateName(state());
2025 if (t == 'N' || debug > 1) {
2026 nospace << ' ' << message;
2028 nospace << ' ' << message.size() << " bytes";
2032 // Is there a reply expected, some command queued?
2033 if (t == 'R' || t == 'N') {
2034 if (token == -1) { // Default token, user typed in extension command
2035 showMessage(QString::fromLatin1(message), LogMisc);
2038 const int index = indexOfCommand(m_extensionCommandQueue, token);
2040 // Did the command finish? Take off queue and complete, invoke CB
2041 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2043 command->success = true;
2044 command->reply = message;
2046 command->success = false;
2047 command->errorMessage = message;
2050 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2051 command->command.constData(), command->token, m_extensionCommandQueue.size());
2052 if (command->handler)
2053 (this->*(command->handler))(command);
2058 if (what == "debuggee_output") {
2059 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2063 if (what == "event") {
2064 showStatusMessage(QString::fromAscii(message), 5000);
2068 if (what == "session_accessible") {
2069 if (!m_accessible) {
2070 m_accessible = true;
2071 handleSessionAccessible(message.toULong());
2076 if (what == "session_inaccessible") {
2078 m_accessible = false;
2079 handleSessionInaccessible(message.toULong());
2084 if (what == "session_idle") {
2085 handleSessionIdle(message);
2089 if (what == "exception") {
2090 WinException exception;
2092 gdbmi.fromString(message);
2093 exception.fromGdbMI(gdbmi);
2094 const QString message = exception.toString(true);
2095 showStatusMessage(message);
2096 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2097 if (exception.exceptionCode == winExceptionCppException)
2098 showMessage(message + QLatin1Char('\n'), AppOutput);
2106 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2107 enum { CdbPromptLength = 7 };
2109 static inline bool isCdbPrompt(const QByteArray &c)
2111 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2112 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2113 && std::isdigit(c.at(4));
2116 // Check for '<token>32>' or '<token>32<'
2117 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2118 int *token, bool *isStart)
2122 const int tokenPrefixSize = tokenPrefix.size();
2123 const int size = c.size();
2124 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2126 switch (c.at(size - 1)) {
2136 if (!c.startsWith(tokenPrefix))
2139 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2143 void CdbEngine::parseOutputLine(QByteArray line)
2145 // The hooked output callback in the extension suppresses prompts,
2146 // it should happen only in initial and exit stages. Note however that
2147 // if the output is not hooked, sequences of prompts are possible which
2148 // can mix things up.
2149 while (isCdbPrompt(line))
2150 line.remove(0, CdbPromptLength);
2151 // An extension notification (potentially consisting of several chunks)
2152 if (line.startsWith(m_creatorExtPrefix)) {
2153 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2154 const char type = line.at(m_creatorExtPrefix.size());
2156 const int tokenPos = m_creatorExtPrefix.size() + 2;
2157 const int tokenEndPos = line.indexOf('|', tokenPos);
2158 QTC_ASSERT(tokenEndPos != -1, return)
2159 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2161 const int remainingChunksPos = tokenEndPos + 1;
2162 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2163 QTC_ASSERT(remainingChunksEndPos != -1, return)
2164 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2165 // const char 'serviceName'
2166 const int whatPos = remainingChunksEndPos + 1;
2167 const int whatEndPos = line.indexOf('|', whatPos);
2168 QTC_ASSERT(whatEndPos != -1, return)
2169 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2170 // Build up buffer, call handler once last chunk was encountered
2171 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2172 if (remainingChunks == 0) {
2173 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2174 m_extensionMessageBuffer.clear();
2178 // Check for command start/end tokens within which the builtin command
2179 // output is enclosed
2181 bool isStartToken = false;
2182 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2184 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2185 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2187 // If there is a current command, wait for end of output indicated by token,
2188 // command, trigger handler and finish, else append to its output.
2189 if (m_currentBuiltinCommandIndex != -1) {
2190 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2191 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2192 if (isCommandToken) {
2193 // Did the command finish? Invoke callback and remove from queue.
2195 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2196 currentCommand->command.constData(), currentCommand->token,
2197 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2198 QTC_ASSERT(token == currentCommand->token, return; );
2199 if (currentCommand->handler)
2200 (this->*(currentCommand->handler))(currentCommand);
2201 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2202 m_currentBuiltinCommandIndex = -1;
2204 // Record output of current command
2205 currentCommand->reply.push_back(line);
2208 } // m_currentCommandIndex
2209 if (isCommandToken) {
2210 // Beginning command token encountered, start to record output.
2211 const int index = indexOfCommand(m_builtinCommandQueue, token);
2212 QTC_ASSERT(isStartToken && index != -1, return; );
2213 m_currentBuiltinCommandIndex = index;
2214 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2216 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2220 showMessage(QString::fromLocal8Bit(line), LogMisc);
2223 void CdbEngine::readyReadStandardOut()
2225 if (m_ignoreCdbOutput)
2227 m_outputBuffer += m_process.readAllStandardOutput();
2228 // Split into lines and parse line by line.
2230 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2231 if (endOfLinePos == -1) {
2235 QByteArray line = m_outputBuffer.left(endOfLinePos);
2236 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2237 line.truncate(line.size() - 1);
2238 parseOutputLine(line);
2239 m_outputBuffer.remove(0, endOfLinePos + 1);
2244 void CdbEngine::readyReadStandardError()
2246 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2249 void CdbEngine::processError()
2251 showMessage(m_process.errorString(), LogError);
2255 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2256 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2258 QByteArray cmd(cmdC);
2259 ByteArrayInputStream str(cmd);
2260 foreach(const BreakpointData *bp, bps)
2261 str << ' ' << bp->bpNumber;
2266 bool CdbEngine::stateAcceptsBreakpointChanges() const
2270 case InferiorStopOk:
2278 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2280 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2281 if (!DebuggerEngine::isCppBreakpoint(data))
2283 switch (data.type) {
2285 case BreakpointAtFork:
2286 //case BreakpointAtVFork:
2287 case BreakpointAtSysCall:
2290 case BreakpointByFileAndLine:
2291 case BreakpointByFunction:
2292 case BreakpointByAddress:
2293 case BreakpointAtThrow:
2294 case BreakpointAtCatch:
2295 case BreakpointAtMain:
2296 case BreakpointAtExec:
2302 void CdbEngine::attemptBreakpointSynchronization()
2305 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2306 // Check if there is anything to be done at all.
2307 BreakHandler *handler = breakHandler();
2308 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2309 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2310 if (acceptsBreakpoint(id))
2311 handler->setEngine(id, this);
2313 // Quick check: is there a need to change something? - Populate module cache
2314 bool changed = false;
2315 const BreakpointIds ids = handler->engineBreakpointIds(this);
2316 foreach (BreakpointId id, ids) {
2317 switch (handler->state(id)) {
2318 case BreakpointInsertRequested:
2319 case BreakpointRemoveRequested:
2320 case BreakpointChangeRequested:
2323 case BreakpointInserted: {
2324 // Collect the new modules matching the files.
2325 // In the future, that information should be obtained from the build system.
2326 const BreakpointParameters &data = handler->breakpointData(id);
2327 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2328 m_fileNameModuleHash.insert(data.fileName, data.module);
2336 if (debugBreakpoints)
2337 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2338 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2342 if (!m_accessible) {
2344 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2345 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2348 // Add/Change breakpoints and store pending ones in map, since
2349 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2350 // handleBreakPoints will the complete that information and set it on the break handler.
2351 bool addedChanged = false;
2352 foreach (BreakpointId id, ids) {
2353 BreakpointParameters parameters = handler->breakpointData(id);
2354 BreakpointResponse response;
2355 response.fromParameters(parameters);
2356 // If we encountered that file and have a module for it: Add it.
2357 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2358 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2359 if (it != m_fileNameModuleHash.constEnd())
2360 parameters.module = it.value();
2362 switch (handler->state(id)) {
2363 case BreakpointInsertRequested:
2364 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2365 if (!parameters.enabled)
2366 postCommand("bd " + QByteArray::number(id), 0);
2367 handler->notifyBreakpointInsertProceeding(id);
2368 handler->notifyBreakpointInsertOk(id);
2369 m_pendingBreakpointMap.insert(id, response);
2370 addedChanged = true;
2371 // Ensure enabled/disabled is correct in handler and line number is there.
2372 handler->setResponse(id, response);
2373 if (debugBreakpoints)
2374 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2376 case BreakpointChangeRequested:
2377 handler->notifyBreakpointChangeProceeding(id);
2378 if (debugBreakpoints)
2379 qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2380 qPrintable(parameters.toString()));
2381 if (parameters.enabled != handler->response(id).enabled) {
2382 // Change enabled/disabled breakpoints without triggering update.
2383 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2384 response.pending = false;
2385 response.enabled = parameters.enabled;
2386 handler->setResponse(id, response);
2388 // Delete and re-add, triggering update
2389 addedChanged = true;
2390 postCommand("bc " + QByteArray::number(id), 0);
2391 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2392 m_pendingBreakpointMap.insert(id, response);
2394 handler->notifyBreakpointChangeOk(id);
2396 case BreakpointRemoveRequested:
2397 postCommand("bc " + QByteArray::number(id), 0);
2398 handler->notifyBreakpointRemoveProceeding(id);
2399 handler->notifyBreakpointRemoveOk(id);
2400 m_pendingBreakpointMap.remove(id);
2406 // List breakpoints and send responses
2408 postCommandSequence(CommandListBreakPoints);
2411 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2412 // manager to correctly process it) and convert to clean path.
2413 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2416 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2417 if (it != m_normalizedFileCache.constEnd())
2419 if (debugSourceMapping)
2420 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2421 // Do we have source path mappings? ->Apply.
2422 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2424 // Up/lower case normalization according to Windows.
2426 QString normalized = winNormalizeFileName(fileName);
2428 QString normalized = fileName;
2430 if (debugSourceMapping)
2431 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2432 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2433 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2434 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2436 // At least upper case drive letter if failed.
2437 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2438 result.fileName[0] = result.fileName.at(0).toUpper();
2440 m_normalizedFileCache.insert(f, result);
2441 if (debugSourceMapping)
2442 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2446 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2447 // has more processing.
2448 static StackFrames parseFrames(const GdbMi &gdbmi)
2451 const int count = gdbmi.childCount();
2453 for (int i = 0; i < count; i++) {
2454 const GdbMi &frameMi = gdbmi.childAt(i);
2457 const GdbMi fullName = frameMi.findChild("fullname");
2458 if (fullName.isValid()) {
2459 frame.file = QFile::decodeName(fullName.data());
2460 frame.line = frameMi.findChild("line").data().toInt();
2461 frame.usable = false; // To be decided after source path mapping.
2463 frame.function = QLatin1String(frameMi.findChild("func").data());
2464 frame.from = QLatin1String(frameMi.findChild("from").data());
2465 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2466 rc.push_back(frame);
2471 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2473 // Parse frames, find current. Special handling for step into:
2474 // When stepping into on an actual function (source mode) by executing 't', an assembler
2475 // frame pointing at the jmp instruction is hit (noticeable by top function being
2476 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2477 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2479 StackFrames frames = parseFrames(data);
2480 const int count = frames.size();
2481 for (int i = 0; i < count; i++) {
2482 const bool hasFile = !frames.at(i).file.isEmpty();
2483 // jmp-frame hit by step into, do another 't' and abort sequence.
2484 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2485 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2486 return ParseStackStepInto;
2489 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2490 frames[i].file = fileName.fileName;
2491 frames[i].usable = fileName.exists;
2492 if (current == -1 && frames[i].usable)
2496 if (count && current == -1) // No usable frame, use assembly.
2499 stackHandler()->setFrames(frames);
2500 activateFrame(current);
2504 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2506 if (command->success) {
2508 data.fromString(command->reply);
2509 parseStackTrace(data, false);
2510 postCommandSequence(command->commandSequence);
2512 showMessage(command->errorMessage, LogError);
2516 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2518 postCommandSequence(command->commandSequence);
2521 // Post a sequence of standard commands: Trigger next once one completes successfully
2522 void CdbEngine::postCommandSequence(unsigned mask)
2525 qDebug("postCommandSequence 0x%x\n", mask);
2529 if (mask & CommandListThreads) {
2530 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2533 if (mask & CommandListStack) {
2534 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2537 if (mask & CommandListRegisters) {
2538 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2539 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2542 if (mask & CommandListModules) {
2543 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2546 if (mask & CommandListBreakPoints) {
2547 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2548 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2553 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2555 bool success = false;
2558 if (!reply->success) {
2559 message = QString::fromAscii(reply->errorMessage);
2562 // Should be "namespace::QWidget:0x555"
2563 QString watchExp = QString::fromAscii(reply->reply);
2564 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2566 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2569 // 0x000 -> nothing found
2570 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2571 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2574 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2575 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2576 watchExp.insert(0, QLatin1String("*("));
2577 watchHandler()->watchExpression(watchExp);
2581 showMessage(message, LogWarning);
2582 m_watchPointX = m_watchPointY = 0;
2585 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2588 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2592 str.setIntegerBase(16);
2593 str << ", at 0x" << r.address;
2594 str.setIntegerBase(10);
2597 str << ", disabled";
2598 if (!r.module.isEmpty())
2599 str << ", module: '" << r.module << '\'';
2603 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2605 if (debugBreakpoints)
2606 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2607 if (!reply->success) {
2608 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2612 value.fromString(reply->reply);
2613 if (value.type() != GdbMi::List) {
2614 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2617 handleBreakPoints(value);
2620 void CdbEngine::handleBreakPoints(const GdbMi &value)
2622 // Report all obtained parameters back. Note that not all parameters are reported
2623 // back, so, match by id and complete
2624 if (debugBreakpoints)
2625 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2627 QTextStream str(&message);
2628 BreakHandler *handler = breakHandler();
2629 foreach (const GdbMi &breakPointG, value.children()) {
2630 BreakpointResponse reportedResponse;
2631 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2632 if (debugBreakpoints)
2633 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2634 qPrintable(reportedResponse.toString()));
2636 if (!reportedResponse.pending) {
2637 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2638 if (it != m_pendingBreakpointMap.end()) {
2639 // Complete the response and set on handler.
2640 BreakpointResponse ¤tResponse = it.value();
2641 currentResponse.number = reportedResponse.number;
2642 currentResponse.address = reportedResponse.address;
2643 currentResponse.module = reportedResponse.module;
2644 currentResponse.pending = reportedResponse.pending;
2645 currentResponse.enabled = reportedResponse.enabled;
2646 formatCdbBreakPointResponse(id, currentResponse, str);
2647 if (debugBreakpoints)
2648 qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2649 handler->setResponse(id, currentResponse);
2650 m_pendingBreakpointMap.erase(it);
2652 } // not pending reported
2654 if (m_pendingBreakpointMap.empty()) {
2655 str << QLatin1String("All breakpoints have been resolved.\n");
2657 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2659 showMessage(message, LogMisc);
2662 void CdbEngine::watchPoint(const QPoint &p)
2664 m_watchPointX = p.x();
2665 m_watchPointY = p.y();
2667 case InferiorStopOk:
2668 postWidgetAtCommand();
2671 // "Select Widget to Watch" from a running application is currently not
2672 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2673 // but requires some work as not to confuse the engine by state-change notifications
2674 // emitted by the debuggee function call.
2675 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2678 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2679 arg(QString::fromAscii(stateName(state()))), LogWarning);
2684 void CdbEngine::postWidgetAtCommand()
2686 QByteArray arguments = QByteArray::number(m_watchPointX);
2687 arguments.append(' ');
2688 arguments.append(QByteArray::number(m_watchPointY));
2689 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2692 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2694 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2695 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2696 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2701 } // namespace Internal
2702 } // namespace Debugger