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::WindowsMsvcFlavor;
354 return sp.debuggerCommand;
356 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
359 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
362 if (!isCdbEngineEnabled()) {
363 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
364 arg(sp.toolChainAbi.toString()));
365 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
366 check->settingsPage = CdbOptionsPage::settingsId();
370 if (!validMode(sp.startMode)) {
371 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
375 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
376 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
377 arg(sp.toolChainAbi.toString()));
381 if (cdbBinary(sp).isEmpty()) {
382 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
383 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
384 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
391 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
396 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
399 opts->push_back(new CdbOptionsPage);
405 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
407 static inline Utils::SavedAction *theAssemblerAction()
409 return debuggerCore()->action(OperateByInstruction);
412 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
413 DebuggerEngine *masterEngine, const OptionsPtr &options) :
414 DebuggerEngine(sp, masterEngine),
415 m_creatorExtPrefix("<qtcreatorcdbext>|"),
416 m_tokenPrefix("<token>"),
418 m_effectiveStartMode(NoStartMode),
421 m_specialStopMode(NoSpecialStop),
422 m_nextCommandToken(0),
423 m_currentBuiltinCommandIndex(-1),
424 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
425 m_operateByInstructionPending(true),
426 m_operateByInstruction(true), // Default CDB setting
427 m_notifyEngineShutdownOnTermination(false),
428 m_hasDebuggee(false),
430 m_sourceStepInto(false),
433 m_ignoreCdbOutput(false)
435 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
437 setObjectName(QLatin1String("CdbEngine"));
438 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
439 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
440 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
441 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
444 void CdbEngine::init()
446 m_effectiveStartMode = NoStartMode;
448 m_accessible = false;
449 m_specialStopMode = NoSpecialStop;
450 m_nextCommandToken = 0;
451 m_currentBuiltinCommandIndex = -1;
452 m_operateByInstructionPending = theAssemblerAction()->isChecked();
453 m_operateByInstruction = true; // Default CDB setting
454 m_notifyEngineShutdownOnTermination = false;
455 m_hasDebuggee = false;
456 m_sourceStepInto = false;
457 m_watchPointX = m_watchPointY = 0;
458 m_ignoreCdbOutput = false;
460 m_outputBuffer.clear();
461 m_builtinCommandQueue.clear();
462 m_extensionCommandQueue.clear();
463 m_extensionMessageBuffer.clear();
464 m_pendingBreakpointMap.clear();
465 m_customSpecialStopData.clear();
467 // Create local list of mappings in native separators
468 m_sourcePathMappings.clear();
469 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
470 if (!globalOptions->sourcePathMap.isEmpty()) {
471 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
472 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
473 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
474 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
475 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
476 QDir::toNativeSeparators(it.value())));
479 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
482 CdbEngine::~CdbEngine()
486 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
488 if (state() == InferiorStopOk) {
489 syncOperateByInstruction(operateByInstruction);
491 // To be set next time session becomes accessible
492 m_operateByInstructionPending = operateByInstruction;
496 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
499 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
500 if (m_operateByInstruction == operateByInstruction)
502 QTC_ASSERT(m_accessible, return; )
503 m_operateByInstruction = operateByInstruction;
504 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
505 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
508 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
509 TextEditor::ITextEditor *editor,
510 const DebuggerToolTipContext &contextIn)
513 qDebug() << Q_FUNC_INFO;
514 // Need a stopped debuggee and a cpp file in a valid frame
515 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
517 // Determine expression and function
520 DebuggerToolTipContext context = contextIn;
521 const QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
522 // Are we in the current stack frame
523 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
525 // No numerical or any other expressions [yet]
526 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
528 const QByteArray iname = QByteArray(localsPrefixC) + exp.toAscii();
529 const QModelIndex index = watchHandler()->itemIndex(iname);
530 if (!index.isValid())
532 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
533 tw->setContext(context);
534 tw->setDebuggerModel(LocalsWatch);
535 tw->setExpression(exp);
536 tw->acquireEngine(this);
537 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
541 // Determine full path to the CDB extension library.
542 QString CdbEngine::extensionLibraryName(bool is64Bit)
544 // Determine extension lib name and path to use
546 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
547 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
548 << '/' << QT_CREATOR_CDB_EXT << ".dll";
552 // Determine environment for CDB.exe, start out with run config and
553 // add CDB extension path merged with system value should there be one.
554 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
555 QString cdbExtensionPath)
557 // Determine CDB extension path from Qt Creator
558 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
559 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
560 if (!oldCdbExtensionPath.isEmpty()) {
561 cdbExtensionPath.append(QLatin1Char(';'));
562 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
564 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
565 // config, just to make sure, delete any existing entries
566 const QString cdbExtensionPathVariableAssign =
567 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
568 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
569 if (it->startsWith(cdbExtensionPathVariableAssign)) {
570 it = runConfigEnvironment.erase(it);
576 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
577 QDir::toNativeSeparators(cdbExtensionPath));
578 return runConfigEnvironment;
581 int CdbEngine::elapsedLogTime() const
583 const int elapsed = m_logTime.elapsed();
584 const int delta = elapsed - m_elapsedLogTime;
585 m_elapsedLogTime = elapsed;
589 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
590 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
593 qDebug("startConsole %s", qPrintable(sp.executable));
594 m_consoleStub.reset(new Utils::ConsoleProcess);
595 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
596 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
597 SLOT(consoleStubMessage(QString, bool)));
598 connect(m_consoleStub.data(), SIGNAL(processStarted()),
599 SLOT(consoleStubProcessStarted()));
600 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
601 SLOT(consoleStubExited()));
602 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
603 if (sp.environment.size())
604 m_consoleStub->setEnvironment(sp.environment);
605 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
606 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
612 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
615 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
617 if (state() == EngineSetupRequested) {
618 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
619 notifyEngineSetupFailed();
621 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
624 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
626 showMessage(msg, AppOutput);
630 void CdbEngine::consoleStubProcessStarted()
633 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
634 // Attach to console process.
635 DebuggerStartParameters attachParameters = startParameters();
636 attachParameters.executable.clear();
637 attachParameters.processArgs.clear();
638 attachParameters.attachPID = m_consoleStub->applicationPID();
639 attachParameters.startMode = AttachExternal;
640 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
641 QString errorMessage;
642 if (!launchCDB(attachParameters, &errorMessage)) {
643 showMessage(errorMessage, LogError);
644 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
645 notifyEngineSetupFailed();
649 void CdbEngine::consoleStubExited()
653 void CdbEngine::setupEngine()
656 qDebug(">setupEngine");
657 // Nag to add symbol server
658 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
659 &(m_options->symbolPaths)))
660 m_options->toSettings(Core::ICore::instance()->settings());
663 if (!m_logTime.elapsed())
665 QString errorMessage;
666 // Console: Launch the stub with the suspended application and attach to it
667 // CDB in theory has a command line option '-2' that launches a
668 // console, too, but that immediately closes when the debuggee quits.
669 // Use the Creator stub instead.
670 const DebuggerStartParameters &sp = startParameters();
671 const bool launchConsole = isConsole(sp);
672 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
673 const bool ok = launchConsole ?
674 startConsole(startParameters(), &errorMessage) :
675 launchCDB(startParameters(), &errorMessage);
677 qDebug("<setupEngine ok=%d", ok);
679 showMessage(errorMessage, LogError);
680 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
681 notifyEngineSetupFailed();
685 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
688 qDebug("launchCDB startMode=%d", sp.startMode);
689 const QChar blank(QLatin1Char(' '));
690 // Start engine which will run until initial breakpoint:
691 // Determine binary (force MSVC), extension lib name and path to use
692 // The extension is passed as relative name with the path variable set
693 //(does not work with absolute path names)
694 const QString executable = cdbBinary(sp);
695 if (executable.isEmpty()) {
696 *errorMessage = tr("There is no CDB executable specified.");
702 Utils::winIs64BitBinary(executable);
706 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
707 if (!extensionFi.isFile()) {
708 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
709 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
712 const QString extensionFileName = extensionFi.fileName();
714 QStringList arguments;
715 const bool isRemote = sp.startMode == AttachToRemote;
716 if (isRemote) { // Must be first
717 arguments << QLatin1String("-remote") << sp.remoteChannel;
719 arguments << (QLatin1String("-a") + extensionFileName);
721 // Source line info/No terminal breakpoint / Pull extension
722 arguments << QLatin1String("-lines") << QLatin1String("-G")
723 // register idle (debuggee stop) notification
724 << QLatin1String("-c")
725 << QString::fromAscii(".idle_cmd " + m_extensionCommandPrefixBA + "idle");
726 if (sp.useTerminal) // Separate console
727 arguments << QLatin1String("-2");
728 if (!m_options->symbolPaths.isEmpty())
729 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
730 if (!m_options->sourcePaths.isEmpty())
731 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
732 // Compile argument string preserving quotes
733 QString nativeArguments = m_options->additionalArguments;
734 switch (sp.startMode) {
737 if (!nativeArguments.isEmpty())
738 nativeArguments.push_back(blank);
739 nativeArguments += QDir::toNativeSeparators(sp.executable);
744 case AttachCrashedExternal:
745 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
746 if (sp.startMode == AttachCrashedExternal)
747 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
750 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
753 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
754 if (!nativeArguments.isEmpty())
755 nativeArguments.push_back(blank);
756 nativeArguments += sp.processArgs;
759 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
760 arg(QDir::toNativeSeparators(executable),
761 arguments.join(QString(blank)) + blank + nativeArguments,
762 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
763 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
764 showMessage(msg, LogMisc);
766 m_outputBuffer.clear();
767 const QStringList environment = sp.environment.size() == 0 ?
768 QProcessEnvironment::systemEnvironment().toStringList() :
769 sp.environment.toStringList();
770 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
771 if (!sp.workingDirectory.isEmpty())
772 m_process.setWorkingDirectory(sp.workingDirectory);
775 if (!nativeArguments.isEmpty()) // Appends
776 m_process.setNativeArguments(nativeArguments);
778 m_process.start(executable, arguments);
779 if (!m_process.waitForStarted()) {
780 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
781 arg(QDir::toNativeSeparators(executable), m_process.errorString());
785 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
787 const unsigned long pid = 0;
789 showMessage(QString::fromLatin1("%1 running as %2").
790 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
791 m_hasDebuggee = true;
792 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
794 const QByteArray loadCommand = QByteArray(".load ")
795 + extensionFileName.toLocal8Bit();
796 postCommand(loadCommand, 0);
797 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
798 notifyEngineSetupOk();
803 void CdbEngine::setupInferior()
806 qDebug("setupInferior");
807 attemptBreakpointSynchronization();
808 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
809 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
812 void CdbEngine::runEngine()
816 // Resume the threads frozen by the console stub.
817 if (isConsole(startParameters()))
818 postCommand("~* m", 0);
819 foreach (const QString &breakEvent, m_options->breakEvents)
820 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
824 bool CdbEngine::commandsPending() const
826 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
829 void CdbEngine::shutdownInferior()
832 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
833 isCdbProcessRunning());
835 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
837 qDebug("notifyInferiorShutdownOk");
838 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
839 notifyInferiorShutdownOk();
844 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
846 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
847 notifyInferiorShutdownOk();
849 // A command got stuck.
850 if (commandsPending()) {
851 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
852 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
853 notifyInferiorShutdownFailed();
856 if (!canInterruptInferior()) {
857 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
858 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
859 notifyInferiorShutdownFailed();
862 interruptInferior(); // Calls us again
866 /* shutdownEngine/processFinished:
867 * Note that in the case of launching a process by the debugger, the debugger
868 * automatically quits a short time after reporting the session becoming
869 * inaccessible without debuggee (notifyInferiorExited). In that case,
870 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
871 * as not to confuse the state engine.
874 void CdbEngine::shutdownEngine()
877 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
878 "accessible=%d,commands pending=%d",
879 stateName(state()), isCdbProcessRunning(), m_accessible,
882 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
883 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
884 notifyEngineShutdownOk();
888 // No longer trigger anything from messages
889 m_ignoreCdbOutput = true;
890 // Go for kill if there are commands pending.
891 if (m_accessible && !commandsPending()) {
892 // detach: Wait for debugger to finish.
893 if (m_effectiveStartMode == AttachExternal)
895 // Remote requires a bit more force to quit.
896 if (m_effectiveStartMode == AttachToRemote) {
897 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
898 postCommand("qq", 0);
902 m_notifyEngineShutdownOnTermination = true;
905 // Remote process. No can do, currently
906 m_notifyEngineShutdownOnTermination = true;
907 Utils::SynchronousProcess::stopProcess(m_process);
910 // Lost debuggee, debugger should quit anytime now
911 if (!m_hasDebuggee) {
912 m_notifyEngineShutdownOnTermination = true;
918 void CdbEngine::processFinished()
921 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
922 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
923 m_process.exitStatus(), m_process.exitCode());
925 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
927 showMessage(tr("CDB crashed"), LogError); // not in your life.
929 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
932 if (m_notifyEngineShutdownOnTermination) {
935 qDebug("notifyEngineIll");
936 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
939 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
940 notifyEngineShutdownOk();
943 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
944 notifyEngineSpontaneousShutdown();
948 void CdbEngine::detachDebugger()
950 postCommand(".detach", 0);
953 static inline bool isWatchIName(const QByteArray &iname)
955 return iname.startsWith("watch");
958 void CdbEngine::updateWatchData(const WatchData &dataIn,
959 const WatchUpdateFlags & flags)
961 if (debug || debugLocals || debugWatches)
962 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
963 elapsedLogTime(), m_accessible, stateName(state()),
964 flags.tryIncremental,
965 qPrintable(dataIn.toString()));
967 if (!m_accessible) // Add watch data while running?
971 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
973 ByteArrayInputStream str(args);
974 str << dataIn.iname << " \"" << dataIn.exp << '"';
975 postExtensionCommand("addwatch", args, 0,
976 &CdbEngine::handleAddWatch, 0,
977 qVariantFromValue(dataIn));
981 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
982 WatchData data = dataIn;
983 data.setAllUnneeded();
984 watchHandler()->insertData(data);
987 updateLocalVariable(dataIn.iname);
990 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
992 WatchData item = qvariant_cast<WatchData>(reply->cookie);
994 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
995 if (reply->success) {
996 updateLocalVariable(item.iname);
998 item.setError(tr("Unable to add expression"));
999 watchHandler()->insertData(item);
1000 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1001 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1002 reply->errorMessage), LogError);
1006 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1008 if (debuggerCore()->boolSetting(VerboseLog))
1009 str << blankSeparator << "-v";
1010 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1011 str << blankSeparator << "-c";
1012 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1013 if (!typeFormats.isEmpty())
1014 str << blankSeparator << "-T " << typeFormats;
1015 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1016 if (!individualFormats.isEmpty())
1017 str << blankSeparator << "-I " << individualFormats;
1020 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1022 const bool isWatch = isWatchIName(iname);
1024 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1025 QByteArray localsArguments;
1026 ByteArrayInputStream str(localsArguments);
1027 addLocalsOptions(str);
1029 const int stackFrame = stackHandler()->currentIndex();
1030 if (stackFrame < 0) {
1031 qWarning("Internal error; no stack frame in updateLocalVariable");
1034 str << blankSeparator << stackFrame;
1036 str << blankSeparator << iname;
1037 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1040 unsigned CdbEngine::debuggerCapabilities() const
1042 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1043 |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
1044 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1045 |BreakModuleCapability;
1048 void CdbEngine::executeStep()
1050 if (!m_operateByInstruction)
1051 m_sourceStepInto = true; // See explanation at handleStackTrace().
1052 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1053 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1054 notifyInferiorRunRequested();
1057 void CdbEngine::executeStepOut()
1059 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1060 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1061 notifyInferiorRunRequested();
1064 void CdbEngine::executeNext()
1066 postCommand(QByteArray("p"), 0); // Step over -> p
1067 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1068 notifyInferiorRunRequested();
1071 void CdbEngine::executeStepI()
1076 void CdbEngine::executeNextI()
1081 void CdbEngine::continueInferior()
1083 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1084 notifyInferiorRunRequested();
1085 doContinueInferior();
1088 void CdbEngine::doContinueInferior()
1090 postCommand(QByteArray("g"), 0);
1093 bool CdbEngine::canInterruptInferior() const
1095 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1098 void CdbEngine::interruptInferior()
1101 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1102 if (canInterruptInferior()) {
1103 doInterruptInferior(NoSpecialStop);
1105 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1106 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1107 notifyInferiorStopOk();
1108 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1109 notifyInferiorRunRequested();
1110 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1111 notifyInferiorRunOk();
1115 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1117 if (m_specialStopMode == NoSpecialStop)
1118 doInterruptInferior(CustomSpecialStop);
1119 m_customSpecialStopData.push_back(v);
1122 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1125 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1126 m_specialStopMode = sm;
1127 QString errorMessage;
1128 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1129 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1130 m_specialStopMode = oldSpecialMode;
1131 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1138 void CdbEngine::executeRunToLine(const ContextData &data)
1140 // Add one-shot breakpoint
1141 BreakpointParameters bp;
1143 bp.type =BreakpointByAddress;
1144 bp.address = data.address;
1146 bp.type =BreakpointByFileAndLine;
1147 bp.fileName = data.fileName;
1148 bp.lineNumber = data.lineNumber;
1150 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1154 void CdbEngine::executeRunToFunction(const QString &functionName)
1156 // Add one-shot breakpoint
1157 BreakpointParameters bp(BreakpointByFunction);
1158 bp.functionName = functionName;
1160 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1164 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1166 const Registers registers = registerHandler()->registers();
1167 QTC_ASSERT(regnr < registers.size(), return)
1168 // Value is decimal or 0x-hex-prefixed
1170 ByteArrayInputStream str(cmd);
1171 str << "r " << registers.at(regnr).name << '=' << value;
1172 postCommand(cmd, 0);
1176 void CdbEngine::executeJumpToLine(const ContextData &data)
1179 // Goto address directly.
1180 jumpToAddress(data.address);
1181 gotoLocation(Location(data.address));
1183 // Jump to source line: Resolve source line address and go to that location
1185 ByteArrayInputStream str(cmd);
1186 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1187 const QVariant cookie = qVariantFromValue(data);
1188 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1192 void CdbEngine::jumpToAddress(quint64 address)
1194 // Fake a jump to address by setting the PC register.
1195 QByteArray registerCmd;
1196 ByteArrayInputStream str(registerCmd);
1197 // PC-register depending on 64/32bit.
1198 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1199 str.setHexPrefix(true);
1200 str.setIntegerBase(16);
1202 postCommand(registerCmd, 0);
1205 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1207 if (cmd->reply.isEmpty())
1209 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1210 // Set register 'rip' to hex address and goto lcoation
1211 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1212 const int equalPos = answer.indexOf(" = ");
1215 answer.remove(0, equalPos + 3);
1216 answer.remove(QLatin1Char('`'));
1218 const quint64 address = answer.toLongLong(&ok, 16);
1219 if (ok && address) {
1220 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1221 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1222 jumpToAddress(address);
1223 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1227 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1230 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1232 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1233 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1238 ByteArrayInputStream str(cmd);
1239 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1240 postCommand(cmd, 0);
1241 // Update all locals in case we change a union or something pointed to
1242 // that affects other variables, too.
1246 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1248 int currentThreadId;
1249 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1250 threadsHandler()->setThreads(threads);
1251 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1252 forceCurrentThreadId : currentThreadId);
1255 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1258 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1259 if (reply->success) {
1261 data.fromString(reply->reply);
1263 // Continue sequence
1264 postCommandSequence(reply->commandSequence);
1266 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1270 void CdbEngine::executeDebuggerCommand(const QString &command)
1272 postCommand(command.toLocal8Bit(), QuietCommand);
1275 // Post command without callback
1276 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1279 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1280 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1281 if (!(flags & QuietCommand))
1282 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1283 m_process.write(cmd + '\n');
1286 // Post a built-in-command producing free-format output with a callback.
1287 // In order to catch the output, it is enclosed in 'echo' commands
1288 // printing a specially formatted token to be identifiable in the output.
1289 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1290 BuiltinCommandHandler handler,
1291 unsigned nextCommandFlag,
1292 const QVariant &cookie)
1294 if (!m_accessible) {
1295 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1296 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1297 showMessage(msg, LogError);
1300 if (!flags & QuietCommand)
1301 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1303 const int token = m_nextCommandToken++;
1304 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1306 m_builtinCommandQueue.push_back(pendingCommand);
1307 // Enclose command in echo-commands for token
1309 ByteArrayInputStream str(fullCmd);
1310 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1311 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1313 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1314 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1315 m_builtinCommandQueue.size(), nextCommandFlag);
1317 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1318 fullCmd.constData());
1319 m_process.write(fullCmd);
1322 // Post an extension command producing one-line output with a callback,
1323 // pass along token for identification in queue.
1324 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1325 const QByteArray &arguments,
1327 ExtensionCommandHandler handler,
1328 unsigned nextCommandFlag,
1329 const QVariant &cookie)
1331 if (!m_accessible) {
1332 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1333 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1334 showMessage(msg, LogError);
1338 const int token = m_nextCommandToken++;
1340 // Format full command with token to be recognizeable in the output
1342 ByteArrayInputStream str(fullCmd);
1343 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1344 if (!arguments.isEmpty())
1345 str << ' ' << arguments;
1347 if (!flags & QuietCommand)
1348 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1350 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1352 m_extensionCommandQueue.push_back(pendingCommand);
1353 // Enclose command in echo-commands for token
1355 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1356 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1357 m_extensionCommandQueue.size(), nextCommandFlag);
1358 m_process.write(fullCmd + '\n');
1361 void CdbEngine::activateFrame(int index)
1363 // TODO: assembler,etc
1366 const StackFrames &frames = stackHandler()->frames();
1367 QTC_ASSERT(index < frames.size(), return; )
1369 const StackFrame frame = frames.at(index);
1370 if (debug || debugLocals)
1371 qDebug("activateFrame idx=%d '%s' %d", index,
1372 qPrintable(frame.file), frame.line);
1373 stackHandler()->setCurrentIndex(index);
1374 const bool showAssembler = !frames.at(index).isUsable();
1375 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1376 watchHandler()->beginCycle();
1377 watchHandler()->endCycle();
1378 QAction *assemblerAction = theAssemblerAction();
1379 if (assemblerAction->isChecked()) {
1380 gotoLocation(frame);
1382 assemblerAction->trigger(); // Seems to trigger update
1385 gotoLocation(frame);
1390 void CdbEngine::updateLocals(bool forNewStackFrame)
1392 typedef QHash<QByteArray, int> WatcherHash;
1394 const int frameIndex = stackHandler()->currentIndex();
1395 if (frameIndex < 0) {
1396 watchHandler()->beginCycle();
1397 watchHandler()->endCycle();
1400 const StackFrame frame = stackHandler()->currentFrame();
1401 if (!frame.isUsable()) {
1402 watchHandler()->beginCycle();
1403 watchHandler()->endCycle();
1406 /* Watchers: Forcibly discard old symbol group as switching from
1407 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1408 * and cause errors as it seems to go 'stale' when switching threads.
1409 * Initial expand, get uninitialized and query */
1410 QByteArray arguments;
1411 ByteArrayInputStream str(arguments);
1414 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1415 if (!expanded.isEmpty()) {
1416 str << blankSeparator << "-e ";
1418 foreach(const QByteArray &e, expanded) {
1424 addLocalsOptions(str);
1425 // Uninitialized variables if desired
1426 if (debuggerCore()->boolSetting(UseCodeModel)) {
1427 QStringList uninitializedVariables;
1428 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1429 frame.function, frame.file, frame.line, &uninitializedVariables);
1430 if (!uninitializedVariables.isEmpty()) {
1431 str << blankSeparator << "-u ";
1433 foreach(const QString &u, uninitializedVariables) {
1436 str << localsPrefixC << u;
1440 // Perform watches synchronization
1441 str << blankSeparator << "-W";
1442 const WatcherHash watcherHash = WatchHandler::watcherNames();
1443 if (!watcherHash.isEmpty()) {
1444 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1445 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1446 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1450 // Required arguments: frame
1451 str << blankSeparator << frameIndex;
1452 watchHandler()->beginCycle();
1453 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1456 void CdbEngine::selectThread(int index)
1458 if (index < 0 || index == threadsHandler()->currentThread())
1462 const int newThreadId = threadsHandler()->threads().at(index).id;
1463 threadsHandler()->setCurrentThread(index);
1465 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1466 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1469 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1471 QTC_ASSERT(m_accessible, return;)
1473 ByteArrayInputStream str(cmd);
1474 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1475 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1476 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1479 // Parse: "00000000`77606060 cc int 3"
1480 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1482 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1483 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1484 DisassemblerLines disassemblerLines;
1485 foreach(const QByteArray &line, command->reply)
1486 disassemblerLines.appendLine(DisassemblerLine(QString::fromLatin1(line)));
1487 agent->setContents(disassemblerLines);
1490 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1492 if (!m_accessible) {
1493 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1497 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1500 ByteArrayInputStream str(args);
1501 str << addr << ' ' << length;
1502 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1503 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1506 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1508 QTC_ASSERT(!data.isEmpty(), return; )
1509 if (!m_accessible) {
1510 const MemoryChangeCookie cookie(addr, data);
1511 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1513 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1517 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1519 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1520 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1521 if (command->success) {
1522 const QByteArray data = QByteArray::fromBase64(command->reply);
1523 if (unsigned(data.size()) == memViewCookie.length)
1524 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1525 memViewCookie.address, data);
1527 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1531 void CdbEngine::reloadModules()
1533 postCommandSequence(CommandListModules);
1536 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1540 void CdbEngine::loadAllSymbols()
1544 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1546 Q_UNUSED(moduleName)
1549 void CdbEngine::reloadRegisters()
1551 postCommandSequence(CommandListRegisters);
1554 void CdbEngine::reloadSourceFiles()
1558 void CdbEngine::reloadFullStack()
1561 qDebug("%s", Q_FUNC_INFO);
1562 postCommandSequence(CommandListStack);
1565 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1567 if (reply->success) {
1568 m_inferiorPid = reply->reply.toUInt();
1569 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1570 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1571 notifyInferiorSetupOk();
1573 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1574 arg(QLatin1String(reply->errorMessage)), LogError);
1575 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1576 notifyInferiorSetupFailed();
1580 // Parse CDB gdbmi register syntax
1581 static inline Register parseRegister(const GdbMi &gdbmiReg)
1584 reg.name = gdbmiReg.findChild("name").data();
1585 const GdbMi description = gdbmiReg.findChild("description");
1586 if (description.type() != GdbMi::Invalid) {
1588 reg.name += description.data();
1591 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1595 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1597 if (reply->success) {
1599 value.fromString(reply->reply);
1600 if (value.type() == GdbMi::List) {
1602 modules.reserve(value.childCount());
1603 foreach (const GdbMi &gdbmiModule, value.children()) {
1605 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1606 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1607 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1608 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1609 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1610 module.symbolsRead = Module::ReadOk;
1611 modules.push_back(module);
1613 modulesHandler()->setModules(modules);
1615 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1616 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1619 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1620 arg(QLatin1String(reply->errorMessage)), LogError);
1622 postCommandSequence(reply->commandSequence);
1626 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1628 if (reply->success) {
1630 value.fromString(reply->reply);
1631 if (value.type() == GdbMi::List) {
1632 Registers registers;
1633 registers.reserve(value.childCount());
1634 foreach (const GdbMi &gdbmiReg, value.children())
1635 registers.push_back(parseRegister(gdbmiReg));
1636 registerHandler()->setRegisters(registers);
1638 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1639 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1642 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1643 arg(QLatin1String(reply->errorMessage)), LogError);
1645 postCommandSequence(reply->commandSequence);
1648 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1650 if (reply->success) {
1651 QList<WatchData> watchData;
1653 root.fromString(reply->reply);
1654 QTC_ASSERT(root.isList(), return ; )
1656 qDebug() << root.toString(true, 4);
1658 // Courtesy of GDB engine
1659 foreach (const GdbMi &child, root.children()) {
1661 dummy.iname = child.findChild("iname").data();
1662 dummy.name = QLatin1String(child.findChild("name").data());
1663 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1665 watchHandler()->insertBulkData(watchData);
1666 watchHandler()->endCycle();
1668 QDebug nsp = qDebug().nospace();
1669 nsp << "Obtained " << watchData.size() << " items:\n";
1670 foreach (const WatchData &wd, watchData)
1671 nsp << wd.toString() <<'\n';
1673 const bool forNewStackFrame = reply->cookie.toBool();
1674 if (forNewStackFrame)
1675 emit stackFrameCompleted();
1677 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1681 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1683 if (!reply->success)
1684 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1687 enum CdbExecutionStatus {
1688 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1689 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1690 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1691 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1692 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1693 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1694 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1696 static const char *cdbStatusName(unsigned long s)
1699 case CDB_STATUS_NO_CHANGE:
1703 case CDB_STATUS_GO_HANDLED:
1704 return "go_handled";
1705 case CDB_STATUS_GO_NOT_HANDLED:
1706 return "go_not_handled";
1707 case CDB_STATUS_STEP_OVER:
1709 case CDB_STATUS_STEP_INTO:
1711 case CDB_STATUS_BREAK:
1713 case CDB_STATUS_NO_DEBUGGEE:
1714 return "no_debuggee";
1715 case CDB_STATUS_STEP_BRANCH:
1716 return "step_branch";
1717 case CDB_STATUS_IGNORE_EVENT:
1718 return "ignore_event";
1719 case CDB_STATUS_RESTART_REQUESTED:
1720 return "restart_requested";
1721 case CDB_STATUS_REVERSE_GO:
1722 return "reverse_go";
1723 case CDB_STATUS_REVERSE_STEP_BRANCH:
1724 return "reverse_step_branch";
1725 case CDB_STATUS_REVERSE_STEP_OVER:
1726 return "reverse_step_over";
1727 case CDB_STATUS_REVERSE_STEP_INTO:
1728 return "reverse_step_into";
1733 /* Examine how to react to a stop. */
1734 enum StopActionFlags
1737 StopReportLog = 0x1,
1738 StopReportStatusMessage = 0x2,
1739 StopReportParseError = 0x4,
1740 StopShowExceptionMessageBox = 0x8,
1741 // Notify stop or just continue
1742 StopNotifyStop = 0x10,
1743 StopIgnoreContinue = 0x20,
1744 // Hit on break in artificial stop thread (created by DebugBreak()).
1745 StopInArtificialThread = 0x40,
1746 StopShutdownInProgress = 0x80 // Shutdown in progress
1749 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1751 QString *exceptionBoxMessage)
1753 // Report stop reason (GDBMI)
1755 if (targetState() == DebuggerFinished)
1756 rc |= StopShutdownInProgress;
1758 qDebug("%s", stopReason.toString(true, 4).constData());
1759 const QByteArray reason = stopReason.findChild("reason").data();
1760 if (reason.isEmpty()) {
1761 *message = tr("Malformed stop response received.");
1762 rc |= StopReportParseError|StopNotifyStop;
1765 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1766 if (state() == InferiorStopOk) {
1767 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1768 arg(QString::fromAscii(reason));
1769 rc |= StopReportLog;
1772 const int threadId = stopReason.findChild("threadId").data().toInt();
1773 if (reason == "breakpoint") {
1774 // Note: Internal breakpoints (run to line) are reported with id=0.
1775 BreakpointId id = 0;
1777 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1778 if (breakpointIdG.isValid()) {
1779 id = breakpointIdG.data().toULongLong();
1781 number = breakHandler()->response(id).number;
1783 if (id && breakHandler()->type(id) == Watchpoint) {
1784 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1786 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1788 rc |= StopReportStatusMessage|StopNotifyStop;
1791 if (reason == "exception") {
1792 WinException exception;
1793 exception.fromGdbMI(stopReason);
1794 QString description = exception.toString();
1796 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1797 // pulls DLLs. Avoid showing a 'stopped' Message box.
1798 if (exception.exceptionCode == winExceptionStartupCompleteTrap
1799 || exception.exceptionCode == winExceptionWX86Breakpoint)
1800 return StopNotifyStop;
1801 if (exception.exceptionCode == winExceptionCtrlPressed) {
1802 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1803 *message = msgInterrupted();
1804 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1807 if (isDebuggerWinException(exception.exceptionCode)) {
1808 rc |= StopReportStatusMessage|StopNotifyStop;
1809 // Detect interruption by DebugBreak() and force a switch to thread 0.
1810 if (exception.function == "ntdll!DbgBreakPoint")
1811 rc |= StopInArtificialThread;
1812 *message = msgInterrupted();
1816 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1817 *message = description;
1818 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1821 *message = msgStopped(QLatin1String(reason));
1822 rc |= StopReportStatusMessage|StopNotifyStop;
1826 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1832 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1833 elapsedLogTime(), messageBA.constData(),
1834 stateName(state()), m_specialStopMode);
1836 // Switch source level debugging
1837 syncOperateByInstruction(m_operateByInstructionPending);
1839 // Engine-special stop reasons: Breakpoints and setup
1840 const SpecialStopMode specialStopMode = m_specialStopMode;
1842 m_specialStopMode = NoSpecialStop;
1844 switch(specialStopMode) {
1845 case SpecialStopSynchronizeBreakpoints:
1847 qDebug("attemptBreakpointSynchronization in special stop");
1848 attemptBreakpointSynchronization();
1849 doContinueInferior();
1851 case SpecialStopGetWidgetAt:
1852 postWidgetAtCommand();
1854 case CustomSpecialStop:
1855 foreach (const QVariant &data, m_customSpecialStopData)
1856 handleCustomSpecialStop(data);
1857 m_customSpecialStopData.clear();
1858 doContinueInferior();
1864 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1865 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1866 notifyEngineSetupOk();
1870 // Further examine stop and report to user
1872 QString exceptionBoxMessage;
1874 stopReason.fromString(messageBA);
1875 int forcedThreadId = -1;
1876 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage);
1877 // Do the non-blocking log reporting
1878 if (stopFlags & StopReportLog)
1879 showMessage(message, LogMisc);
1880 if (stopFlags & StopReportStatusMessage)
1881 showStatusMessage(message);
1882 if (stopFlags & StopReportParseError)
1883 showMessage(message, LogError);
1884 // Ignore things like WOW64
1885 if (stopFlags & StopIgnoreContinue) {
1886 postCommand("g", 0);
1889 // Notify about state and send off command sequence to get stack, etc.
1890 if (stopFlags & StopNotifyStop) {
1891 if (state() == InferiorStopRequested) {
1892 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1893 notifyInferiorStopOk();
1895 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1896 notifyInferiorSpontaneousStop();
1898 // Prevent further commands from being sent if shutdown is in progress
1899 if (stopFlags & StopShutdownInProgress) {
1900 showMessage(QString::fromLatin1("Shutdown request detected..."));
1903 const bool sourceStepInto = m_sourceStepInto;
1904 m_sourceStepInto = false;
1905 // Start sequence to get all relevant data.
1906 if (stopFlags & StopInArtificialThread) {
1907 showMessage(tr("Switching to main thread..."), LogMisc);
1908 postCommand("~0 s", 0);
1910 // Re-fetch stack again.
1911 postCommandSequence(CommandListStack);
1913 const GdbMi stack = stopReason.findChild("stack");
1914 if (stack.isValid()) {
1915 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1916 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1920 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1923 const GdbMi threads = stopReason.findChild("threads");
1924 if (threads.isValid()) {
1925 parseThreads(threads, forcedThreadId);
1927 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1929 // Fire off remaining commands asynchronously
1930 if (!m_pendingBreakpointMap.isEmpty())
1931 postCommandSequence(CommandListBreakPoints);
1932 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1933 postCommandSequence(CommandListRegisters);
1934 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
1935 postCommandSequence(CommandListModules);
1937 // After the sequence has been sent off and CDB is pondering the commands,
1938 // pop up a message box for exceptions.
1939 if (stopFlags & StopShowExceptionMessageBox)
1940 showStoppedByExceptionMessageBox(exceptionBoxMessage);
1943 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
1945 const DebuggerState s = state();
1946 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
1950 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
1951 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1954 case EngineShutdownRequested:
1957 case InferiorShutdownRequested:
1965 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
1967 const DebuggerState s = state();
1970 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
1974 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
1975 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1978 case EngineSetupRequested:
1980 case EngineRunRequested:
1981 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
1982 notifyEngineRunAndInferiorRunOk();
1985 case InferiorStopOk:
1986 // Inaccessible without debuggee (exit breakpoint)
1987 // We go for spontaneous engine shutdown instead.
1988 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
1990 qDebug("Lost debuggeee");
1991 m_hasDebuggee = false;
1994 case InferiorRunRequested:
1995 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1996 notifyInferiorRunOk();
1999 case EngineShutdownRequested:
2006 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2009 QDebug nospace = qDebug().nospace();
2010 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2011 << ' ' << stateName(state());
2012 if (t == 'N' || debug > 1) {
2013 nospace << ' ' << message;
2015 nospace << ' ' << message.size() << " bytes";
2019 // Is there a reply expected, some command queued?
2020 if (t == 'R' || t == 'N') {
2021 if (token == -1) { // Default token, user typed in extension command
2022 showMessage(QString::fromLatin1(message), LogMisc);
2025 const int index = indexOfCommand(m_extensionCommandQueue, token);
2027 // Did the command finish? Take off queue and complete, invoke CB
2028 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2030 command->success = true;
2031 command->reply = message;
2033 command->success = false;
2034 command->errorMessage = message;
2037 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2038 command->command.constData(), command->token, m_extensionCommandQueue.size());
2039 if (command->handler)
2040 (this->*(command->handler))(command);
2045 if (what == "debuggee_output") {
2046 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2050 if (what == "event") {
2051 showStatusMessage(QString::fromAscii(message), 5000);
2055 if (what == "session_accessible") {
2056 if (!m_accessible) {
2057 m_accessible = true;
2058 handleSessionAccessible(message.toULong());
2063 if (what == "session_inaccessible") {
2065 m_accessible = false;
2066 handleSessionInaccessible(message.toULong());
2071 if (what == "session_idle") {
2072 handleSessionIdle(message);
2076 if (what == "exception") {
2077 WinException exception;
2079 gdbmi.fromString(message);
2080 exception.fromGdbMI(gdbmi);
2081 const QString message = exception.toString(true);
2082 showStatusMessage(message);
2083 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2084 if (exception.exceptionCode == winExceptionCppException)
2085 showMessage(message + QLatin1Char('\n'), AppOutput);
2093 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2094 enum { CdbPromptLength = 7 };
2096 static inline bool isCdbPrompt(const QByteArray &c)
2098 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2099 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2100 && std::isdigit(c.at(4));
2103 // Check for '<token>32>' or '<token>32<'
2104 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2105 int *token, bool *isStart)
2109 const int tokenPrefixSize = tokenPrefix.size();
2110 const int size = c.size();
2111 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2113 switch (c.at(size - 1)) {
2123 if (!c.startsWith(tokenPrefix))
2126 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2130 void CdbEngine::parseOutputLine(QByteArray line)
2132 // The hooked output callback in the extension suppresses prompts,
2133 // it should happen only in initial and exit stages. Note however that
2134 // if the output is not hooked, sequences of prompts are possible which
2135 // can mix things up.
2136 while (isCdbPrompt(line))
2137 line.remove(0, CdbPromptLength);
2138 // An extension notification (potentially consisting of several chunks)
2139 if (line.startsWith(m_creatorExtPrefix)) {
2140 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2141 const char type = line.at(m_creatorExtPrefix.size());
2143 const int tokenPos = m_creatorExtPrefix.size() + 2;
2144 const int tokenEndPos = line.indexOf('|', tokenPos);
2145 QTC_ASSERT(tokenEndPos != -1, return)
2146 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2148 const int remainingChunksPos = tokenEndPos + 1;
2149 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2150 QTC_ASSERT(remainingChunksEndPos != -1, return)
2151 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2152 // const char 'serviceName'
2153 const int whatPos = remainingChunksEndPos + 1;
2154 const int whatEndPos = line.indexOf('|', whatPos);
2155 QTC_ASSERT(whatEndPos != -1, return)
2156 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2157 // Build up buffer, call handler once last chunk was encountered
2158 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2159 if (remainingChunks == 0) {
2160 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2161 m_extensionMessageBuffer.clear();
2165 // Check for command start/end tokens within which the builtin command
2166 // output is enclosed
2168 bool isStartToken = false;
2169 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2171 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2172 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2174 // If there is a current command, wait for end of output indicated by token,
2175 // command, trigger handler and finish, else append to its output.
2176 if (m_currentBuiltinCommandIndex != -1) {
2177 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2178 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2179 if (isCommandToken) {
2180 // Did the command finish? Invoke callback and remove from queue.
2182 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2183 currentCommand->command.constData(), currentCommand->token,
2184 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2185 QTC_ASSERT(token == currentCommand->token, return; );
2186 if (currentCommand->handler)
2187 (this->*(currentCommand->handler))(currentCommand);
2188 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2189 m_currentBuiltinCommandIndex = -1;
2191 // Record output of current command
2192 currentCommand->reply.push_back(line);
2195 } // m_currentCommandIndex
2196 if (isCommandToken) {
2197 // Beginning command token encountered, start to record output.
2198 const int index = indexOfCommand(m_builtinCommandQueue, token);
2199 QTC_ASSERT(isStartToken && index != -1, return; );
2200 m_currentBuiltinCommandIndex = index;
2201 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2203 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2207 showMessage(QString::fromLocal8Bit(line), LogMisc);
2210 void CdbEngine::readyReadStandardOut()
2212 if (m_ignoreCdbOutput)
2214 m_outputBuffer += m_process.readAllStandardOutput();
2215 // Split into lines and parse line by line.
2217 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2218 if (endOfLinePos == -1) {
2222 QByteArray line = m_outputBuffer.left(endOfLinePos);
2223 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2224 line.truncate(line.size() - 1);
2225 parseOutputLine(line);
2226 m_outputBuffer.remove(0, endOfLinePos + 1);
2231 void CdbEngine::readyReadStandardError()
2233 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2236 void CdbEngine::processError()
2238 showMessage(m_process.errorString(), LogError);
2242 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2243 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2245 QByteArray cmd(cmdC);
2246 ByteArrayInputStream str(cmd);
2247 foreach(const BreakpointData *bp, bps)
2248 str << ' ' << bp->bpNumber;
2253 bool CdbEngine::stateAcceptsBreakpointChanges() const
2257 case InferiorStopOk:
2265 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2267 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2268 if (!DebuggerEngine::isCppBreakpoint(data))
2270 switch (data.type) {
2272 case BreakpointAtFork:
2273 case BreakpointAtVFork:
2274 case BreakpointAtSysCall:
2277 case BreakpointByFileAndLine:
2278 case BreakpointByFunction:
2279 case BreakpointByAddress:
2280 case BreakpointAtThrow:
2281 case BreakpointAtCatch:
2282 case BreakpointAtMain:
2283 case BreakpointAtExec:
2289 void CdbEngine::attemptBreakpointSynchronization()
2292 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2293 // Check if there is anything to be done at all.
2294 BreakHandler *handler = breakHandler();
2295 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2296 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2297 if (acceptsBreakpoint(id))
2298 handler->setEngine(id, this);
2300 // Quick check: is there a need to change something? - Populate module cache
2301 bool changed = false;
2302 const BreakpointIds ids = handler->engineBreakpointIds(this);
2303 foreach (BreakpointId id, ids) {
2304 switch (handler->state(id)) {
2305 case BreakpointInsertRequested:
2306 case BreakpointRemoveRequested:
2307 case BreakpointChangeRequested:
2310 case BreakpointInserted: {
2311 // Collect the new modules matching the files.
2312 // In the future, that information should be obtained from the build system.
2313 const BreakpointParameters &data = handler->breakpointData(id);
2314 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2315 m_fileNameModuleHash.insert(data.fileName, data.module);
2323 if (debugBreakpoints)
2324 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2325 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2329 if (!m_accessible) {
2331 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2332 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2335 // Add/Change breakpoints and store pending ones in map, since
2336 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2337 // handleBreakPoints will the complete that information and set it on the break handler.
2338 bool addedChanged = false;
2339 foreach (BreakpointId id, ids) {
2340 BreakpointParameters parameters = handler->breakpointData(id);
2341 BreakpointResponse response;
2342 response.fromParameters(parameters);
2343 // If we encountered that file and have a module for it: Add it.
2344 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2345 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2346 if (it != m_fileNameModuleHash.constEnd())
2347 parameters.module = it.value();
2349 switch (handler->state(id)) {
2350 case BreakpointInsertRequested:
2351 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2352 if (!parameters.enabled)
2353 postCommand("bd " + QByteArray::number(id), 0);
2354 handler->notifyBreakpointInsertProceeding(id);
2355 handler->notifyBreakpointInsertOk(id);
2356 m_pendingBreakpointMap.insert(id, response);
2357 addedChanged = true;
2358 if (debugBreakpoints)
2359 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2361 case BreakpointChangeRequested:
2362 handler->notifyBreakpointChangeProceeding(id);
2363 if (parameters.enabled != handler->response(id).enabled) {
2364 // Change enabled/disabled breakpoints without triggering update.
2365 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2366 response.pending = false;
2367 response.enabled = parameters.enabled;
2368 handler->setResponse(id, response);
2370 // Delete and re-add, triggering update
2371 addedChanged = true;
2372 postCommand("bc " + QByteArray::number(id), 0);
2373 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2374 m_pendingBreakpointMap.insert(id, response);
2376 handler->notifyBreakpointChangeOk(id);
2377 if (debugBreakpoints)
2378 qDebug("Changing %llu %s\n", id, qPrintable(response.toString()));
2380 case BreakpointRemoveRequested:
2381 postCommand("bc " + QByteArray::number(id), 0);
2382 handler->notifyBreakpointRemoveProceeding(id);
2383 handler->notifyBreakpointRemoveOk(id);
2384 m_pendingBreakpointMap.remove(id);
2390 // List breakpoints and send responses
2392 postCommandSequence(CommandListBreakPoints);
2395 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2396 // manager to correctly process it) and convert to clean path.
2397 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2400 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2401 if (it != m_normalizedFileCache.constEnd())
2403 if (debugSourceMapping)
2404 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2405 // Do we have source path mappings? ->Apply.
2406 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2408 // Up/lower case normalization according to Windows.
2410 QString normalized = winNormalizeFileName(fileName);
2412 QString normalized = fileName;
2414 if (debugSourceMapping)
2415 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2416 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2417 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2418 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2420 // At least upper case drive letter if failed.
2421 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2422 result.fileName[0] = result.fileName.at(0).toUpper();
2424 m_normalizedFileCache.insert(f, result);
2425 if (debugSourceMapping)
2426 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2430 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2431 // has more processing.
2432 static StackFrames parseFrames(const GdbMi &gdbmi)
2435 const int count = gdbmi.childCount();
2437 for (int i = 0; i < count; i++) {
2438 const GdbMi &frameMi = gdbmi.childAt(i);
2441 const GdbMi fullName = frameMi.findChild("fullname");
2442 if (fullName.isValid()) {
2443 frame.file = QFile::decodeName(fullName.data());
2444 frame.line = frameMi.findChild("line").data().toInt();
2445 frame.usable = false; // To be decided after source path mapping.
2447 frame.function = QLatin1String(frameMi.findChild("func").data());
2448 frame.from = QLatin1String(frameMi.findChild("from").data());
2449 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2450 rc.push_back(frame);
2455 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2457 // Parse frames, find current. Special handling for step into:
2458 // When stepping into on an actual function (source mode) by executing 't', an assembler
2459 // frame pointing at the jmp instruction is hit (noticeable by top function being
2460 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2461 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2463 StackFrames frames = parseFrames(data);
2464 const int count = frames.size();
2465 for (int i = 0; i < count; i++) {
2466 const bool hasFile = !frames.at(i).file.isEmpty();
2467 // jmp-frame hit by step into, do another 't' and abort sequence.
2468 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2469 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2470 return ParseStackStepInto;
2473 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2474 frames[i].file = fileName.fileName;
2475 frames[i].usable = fileName.exists;
2476 if (current == -1 && frames[i].usable)
2480 if (count && current == -1) // No usable frame, use assembly.
2483 stackHandler()->setFrames(frames);
2484 activateFrame(current);
2488 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2490 if (command->success) {
2492 data.fromString(command->reply);
2493 parseStackTrace(data, false);
2494 postCommandSequence(command->commandSequence);
2496 showMessage(command->errorMessage, LogError);
2500 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2502 postCommandSequence(command->commandSequence);
2505 // Post a sequence of standard commands: Trigger next once one completes successfully
2506 void CdbEngine::postCommandSequence(unsigned mask)
2509 qDebug("postCommandSequence 0x%x\n", mask);
2513 if (mask & CommandListThreads) {
2514 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2517 if (mask & CommandListStack) {
2518 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2521 if (mask & CommandListRegisters) {
2522 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2523 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2526 if (mask & CommandListModules) {
2527 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2530 if (mask & CommandListBreakPoints) {
2531 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2532 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2537 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2539 bool success = false;
2542 if (!reply->success) {
2543 message = QString::fromAscii(reply->errorMessage);
2546 // Should be "namespace::QWidget:0x555"
2547 QString watchExp = QString::fromAscii(reply->reply);
2548 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2550 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2553 // 0x000 -> nothing found
2554 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2555 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2558 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2559 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2560 watchExp.insert(0, QLatin1String("*("));
2561 watchHandler()->watchExpression(watchExp);
2565 showMessage(message, LogWarning);
2566 m_watchPointX = m_watchPointY = 0;
2569 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2572 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2576 str.setIntegerBase(16);
2577 str << ", at 0x" << r.address;
2578 str.setIntegerBase(10);
2581 str << ", disabled";
2582 if (!r.module.isEmpty())
2583 str << ", module: '" << r.module << '\'';
2587 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2589 if (debugBreakpoints)
2590 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2591 if (!reply->success) {
2592 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2596 value.fromString(reply->reply);
2597 if (value.type() != GdbMi::List) {
2598 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2601 handleBreakPoints(value);
2604 void CdbEngine::handleBreakPoints(const GdbMi &value)
2606 // Report all obtained parameters back. Note that not all parameters are reported
2607 // back, so, match by id and complete
2608 if (debugBreakpoints)
2609 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2611 QTextStream str(&message);
2612 BreakHandler *handler = breakHandler();
2613 foreach (const GdbMi &breakPointG, value.children()) {
2614 BreakpointResponse reportedResponse;
2615 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2616 if (debugBreakpoints)
2617 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2618 qPrintable(reportedResponse.toString()));
2620 if (!reportedResponse.pending) {
2621 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2622 if (it != m_pendingBreakpointMap.end()) {
2623 // Complete the response and set on handler.
2624 BreakpointResponse ¤tResponse = it.value();
2625 currentResponse.number = reportedResponse.number;
2626 currentResponse.address = reportedResponse.address;
2627 currentResponse.module = reportedResponse.module;
2628 currentResponse.pending = reportedResponse.pending;
2629 currentResponse.enabled = reportedResponse.enabled;
2630 formatCdbBreakPointResponse(id, currentResponse, str);
2631 handler->setResponse(id, currentResponse);
2632 m_pendingBreakpointMap.erase(it);
2634 } // not pending reported
2636 if (m_pendingBreakpointMap.empty()) {
2637 str << QLatin1String("All breakpoints have been resolved.\n");
2639 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2641 showMessage(message, LogMisc);
2644 void CdbEngine::watchPoint(const QPoint &p)
2646 m_watchPointX = p.x();
2647 m_watchPointY = p.y();
2649 case InferiorStopOk:
2650 postWidgetAtCommand();
2653 // "Select Widget to Watch" from a running application is currently not
2654 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2655 // but requires some work as not to confuse the engine by state-change notifications
2656 // emitted by the debuggee function call.
2657 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2660 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2661 arg(QString::fromAscii(stateName(state()))), LogWarning);
2666 void CdbEngine::postWidgetAtCommand()
2668 QByteArray arguments = QByteArray::number(m_watchPointX);
2669 arguments.append(' ');
2670 arguments.append(QByteArray::number(m_watchPointY));
2671 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2674 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2676 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2677 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2678 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2683 } // namespace Internal
2684 } // namespace Debugger