1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
31 **************************************************************************/
33 #include "cdbengine.h"
34 #include "debuggerstartparameters.h"
35 #include "disassemblerlines.h"
36 #include "cdboptions.h"
37 #include "cdboptionspage.h"
38 #include "bytearrayinputstream.h"
39 #include "breakpoint.h"
40 #include "breakhandler.h"
41 #include "stackframe.h"
42 #include "stackhandler.h"
43 #include "watchhandler.h"
44 #include "threadshandler.h"
45 #include "moduleshandler.h"
46 #include "debuggeractions.h"
47 #include "debuggerinternalconstants.h"
48 #include "debuggercore.h"
49 #include "registerhandler.h"
50 #include "disassembleragent.h"
51 #include "memoryagent.h"
52 #include "debuggerrunner.h"
53 #include "debuggertooltipmanager.h"
54 #include "cdbparsehelpers.h"
55 #include "watchutils.h"
56 #include "gdb/gdbmi.h"
57 #include "shared/cdbsymbolpathlisteditor.h"
59 #include <TranslationUnit.h>
61 #include <coreplugin/icore.h>
62 #include <texteditor/itexteditor.h>
63 #include <projectexplorer/abi.h>
64 #include <projectexplorer/projectexplorerconstants.h>
66 #include <utils/synchronousprocess.h>
67 #include <utils/winutils.h>
68 #include <utils/qtcassert.h>
69 #include <utils/savedaction.h>
70 #include <utils/consoleprocess.h>
71 #include <utils/fileutils.h>
73 #include <cplusplus/findcdbbreakpoint.h>
74 #include <cplusplus/CppDocument.h>
75 #include <cplusplus/ModelManagerInterface.h>
77 #include <QtCore/QCoreApplication>
78 #include <QtCore/QFileInfo>
79 #include <QtCore/QDir>
80 #include <QtCore/QDebug>
81 #include <QtCore/QTextStream>
82 #include <QtCore/QDateTime>
83 #include <QtGui/QToolTip>
84 #include <QtGui/QMainWindow>
85 #include <QtGui/QMessageBox>
88 # include <utils/winutils.h>
89 # include "dbgwinutils.h"
94 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
95 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
98 enum { debugLocals = 0 };
99 enum { debugSourceMapping = 0 };
100 enum { debugWatches = 0 };
101 enum { debugBreakpoints = 0 };
104 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
106 # define STATE_DEBUG(state, func, line, notifyFunc)
110 \class Debugger::Internal::CdbEngine
112 Cdb engine version 2: Run the CDB process on pipes and parse its output.
113 The engine relies on a CDB extension Qt Creator provides as an extension
114 library (32/64bit), which is loaded into cdb.exe. It serves to:
117 \o Notify the engine about the state of the debugging session:
119 \o idle: (hooked up with .idle_cmd) debuggee stopped
120 \o accessible: Debuggee stopped, cdb.exe accepts commands
121 \o inaccessible: Debuggee runs, no way to post commands
122 \o session active/inactive: Lost debuggee, terminating.
124 \o Hook up with output/event callbacks and produce formatted output to be able
125 to catch application output and exceptions.
126 \o Provide some extension commands that produce output in a standardized (GDBMI)
127 format that ends up in handleExtensionMessage(), for example:
129 \o pid Return debuggee pid for interrupting.
130 \o locals Print locals from SymbolGroup
131 \o expandLocals Expand locals in symbol group
132 \o registers, modules, threads
136 Debugger commands can be posted by calling:
140 \o postCommand(): Does not expect a reply
141 \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
142 that is captured by enclosing it in special tokens using the 'echo' command and
143 then invokes a callback with a CdbBuiltinCommand structure.
144 \o postExtensionCommand(): Run a command provided by the extension producing
145 one-line output and invoke a callback with a CdbExtensionCommand structure
146 (output is potentially split up in chunks).
151 [Console: The console stub launches the process. On process startup,
152 launchCDB() is called with AttachExternal].
153 setupEngine() calls launchCDB() with the startparameters. The debuggee
154 runs into the initial breakpoint (session idle). EngineSetupOk is
155 notified (inferior still stopped). setupInferior() is then called
156 which does breakpoint synchronization and issues the extension 'pid'
157 command to obtain the inferior pid (which also hooks up the output callbacks).
158 handlePid() notifies notifyInferiorSetupOk.
159 runEngine() is then called which issues 'g' to continue the inferior.
160 Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
161 when the inferior exits (except attach modes).
164 using namespace ProjectExplorer;
169 static const char localsPrefixC[] = "local.";
171 struct MemoryViewCookie
173 explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
174 quint64 addr = 0, quint64 l = 0) :
175 agent(a), editorToken(e), address(addr), length(l)
179 QObject *editorToken;
184 struct MemoryChangeCookie
186 explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
187 address(addr), data(d) {}
193 struct ConditionalBreakPointCookie
195 ConditionalBreakPointCookie(BreakpointModelId i = BreakpointModelId()) : id(i) {}
196 BreakpointModelId id;
200 } // namespace Internal
201 } // namespace Debugger
203 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
204 Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
205 Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
210 static inline bool isCreatorConsole(const DebuggerStartParameters &sp, const CdbOptions &o)
212 return !o.cdbConsole && sp.useTerminal
213 && (sp.startMode == StartInternal || sp.startMode == StartExternal);
217 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
219 QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
220 debuggerCore()->mainWindow());
221 mb->setAttribute(Qt::WA_DeleteOnClose);
226 // Base data structure for command queue entries with callback
227 struct CdbCommandBase
229 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
232 CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
233 unsigned nc, const QVariant &cookie);
239 // Continue with another commands as specified in CommandSequenceFlags
240 unsigned commandSequence;
243 CdbCommandBase::CdbCommandBase() :
244 token(0), flags(0), commandSequence(0)
248 CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
249 unsigned nc, const QVariant &c) :
250 token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
254 // Queue entry for builtin commands producing free-format
255 // line-by-line output.
256 struct CdbBuiltinCommand : public CdbCommandBase
258 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
260 CdbBuiltinCommand() {}
261 CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
263 unsigned nc, const QVariant &cookie) :
264 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
268 QByteArray joinedReply() const;
270 CommandHandler handler;
271 QList<QByteArray> reply;
274 QByteArray CdbBuiltinCommand::joinedReply() const
279 answer.reserve(120 * reply.size());
280 foreach (const QByteArray &l, reply) {
287 // Queue entry for Qt Creator extension commands producing one-line
288 // output with success flag and error message.
289 struct CdbExtensionCommand : public CdbCommandBase
291 typedef CdbEngine::ExtensionCommandHandler CommandHandler;
293 CdbExtensionCommand() : success(false) {}
294 CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
296 unsigned nc, const QVariant &cookie) :
297 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
299 CommandHandler handler;
301 QByteArray errorMessage;
305 template <class CommandPtrType>
306 int indexOfCommand(const QList<CommandPtrType> &l, int token)
308 const int count = l.size();
309 for (int i = 0; i < count; i++)
310 if (l.at(i)->token == token)
315 static inline bool validMode(DebuggerStartMode sm)
328 // Accessed by RunControlFactory
329 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
330 DebuggerEngine *masterEngine, QString *errorMessage)
333 CdbOptionsPage *op = CdbOptionsPage::instance();
334 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
335 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
338 return new CdbEngine(sp, masterEngine, op->options());
340 Q_UNUSED(masterEngine)
343 *errorMessage = QString::fromLatin1("Unsupported debug mode");
347 bool isCdbEngineEnabled()
350 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
356 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
358 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
361 static QString cdbBinary(const DebuggerStartParameters &sp)
363 if (!sp.debuggerCommand.isEmpty()) {
364 // Do not use a GDB binary if we got started for a project with MinGW runtime.
365 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
366 && (sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2005Flavor
367 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2008Flavor
368 || sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2010Flavor);
370 return sp.debuggerCommand;
372 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
375 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
378 if (!isCdbEngineEnabled()) {
379 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
380 arg(sp.toolChainAbi.toString()));
381 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
382 check->settingsPage = CdbOptionsPage::settingsId();
386 if (!validMode(sp.startMode)) {
387 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
391 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
392 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
393 arg(sp.toolChainAbi.toString()));
397 if (cdbBinary(sp).isEmpty()) {
398 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
399 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
400 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
407 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
412 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
415 opts->push_back(new CdbOptionsPage);
421 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
423 static inline Utils::SavedAction *theAssemblerAction()
425 return debuggerCore()->action(OperateByInstruction);
428 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
429 DebuggerEngine *masterEngine, const OptionsPtr &options) :
430 DebuggerEngine(sp, masterEngine),
431 m_creatorExtPrefix("<qtcreatorcdbext>|"),
432 m_tokenPrefix("<token>"),
434 m_effectiveStartMode(NoStartMode),
436 m_specialStopMode(NoSpecialStop),
437 m_nextCommandToken(0),
438 m_currentBuiltinCommandIndex(-1),
439 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
440 m_operateByInstructionPending(true),
441 m_operateByInstruction(true), // Default CDB setting
442 m_notifyEngineShutdownOnTermination(false),
443 m_hasDebuggee(false),
445 m_sourceStepInto(false),
448 m_ignoreCdbOutput(false)
450 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
452 setObjectName(QLatin1String("CdbEngine"));
453 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
454 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
455 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
456 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
459 void CdbEngine::init()
461 m_effectiveStartMode = NoStartMode;
462 notifyInferiorPid(0);
463 m_accessible = false;
464 m_specialStopMode = NoSpecialStop;
465 m_nextCommandToken = 0;
466 m_currentBuiltinCommandIndex = -1;
467 m_operateByInstructionPending = theAssemblerAction()->isChecked();
468 m_operateByInstruction = true; // Default CDB setting
469 m_notifyEngineShutdownOnTermination = false;
470 m_hasDebuggee = false;
471 m_sourceStepInto = false;
472 m_watchPointX = m_watchPointY = 0;
473 m_ignoreCdbOutput = false;
475 m_outputBuffer.clear();
476 m_builtinCommandQueue.clear();
477 m_extensionCommandQueue.clear();
478 m_extensionMessageBuffer.clear();
479 m_pendingBreakpointMap.clear();
480 m_customSpecialStopData.clear();
481 m_symbolAddressCache.clear();
483 // Create local list of mappings in native separators
484 m_sourcePathMappings.clear();
485 const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
486 if (!globalOptions->sourcePathMap.isEmpty()) {
487 typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
488 m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
489 const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
490 for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
491 m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
492 QDir::toNativeSeparators(it.value())));
495 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
498 CdbEngine::~CdbEngine()
502 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
504 // To be set next time session becomes accessible
505 m_operateByInstructionPending = operateByInstruction;
506 if (state() == InferiorStopOk)
507 syncOperateByInstruction(operateByInstruction);
510 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
513 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
514 if (m_operateByInstruction == operateByInstruction)
516 QTC_ASSERT(m_accessible, return; )
517 m_operateByInstruction = operateByInstruction;
518 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
519 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
522 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
523 TextEditor::ITextEditor *editor,
524 const DebuggerToolTipContext &contextIn)
527 qDebug() << Q_FUNC_INFO;
528 // Need a stopped debuggee and a cpp file in a valid frame
529 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
531 // Determine expression and function
534 DebuggerToolTipContext context = contextIn;
535 QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
536 // Are we in the current stack frame
537 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
539 // No numerical or any other expressions [yet]
540 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
542 // Can this be found as a local variable?
543 const QByteArray localsPrefix(localsPrefixC);
544 QByteArray iname = localsPrefix + exp.toAscii();
545 QModelIndex index = watchHandler()->itemIndex(iname);
546 if (!index.isValid()) {
547 // Nope, try a 'local.this.m_foo'.
548 exp.prepend(QLatin1String("this."));
549 iname.insert(localsPrefix.size(), "this.");
550 index = watchHandler()->itemIndex(iname);
551 if (!index.isValid())
554 DebuggerToolTipWidget *tw = new DebuggerToolTipWidget;
555 tw->setContext(context);
556 tw->setDebuggerModel(LocalsWatch);
557 tw->setExpression(exp);
558 tw->acquireEngine(this);
559 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
563 // Determine full path to the CDB extension library.
564 QString CdbEngine::extensionLibraryName(bool is64Bit)
566 // Determine extension lib name and path to use
568 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
569 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
570 << '/' << QT_CREATOR_CDB_EXT << ".dll";
574 // Determine environment for CDB.exe, start out with run config and
575 // add CDB extension path merged with system value should there be one.
576 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
577 QString cdbExtensionPath)
579 // Determine CDB extension path from Qt Creator
580 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
581 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
582 if (!oldCdbExtensionPath.isEmpty()) {
583 cdbExtensionPath.append(QLatin1Char(';'));
584 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
586 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
587 // config, just to make sure, delete any existing entries
588 const QString cdbExtensionPathVariableAssign =
589 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
590 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
591 if (it->startsWith(cdbExtensionPathVariableAssign)) {
592 it = runConfigEnvironment.erase(it);
598 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
599 QDir::toNativeSeparators(cdbExtensionPath));
600 return runConfigEnvironment;
603 int CdbEngine::elapsedLogTime() const
605 const int elapsed = m_logTime.elapsed();
606 const int delta = elapsed - m_elapsedLogTime;
607 m_elapsedLogTime = elapsed;
611 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
612 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
615 qDebug("startConsole %s", qPrintable(sp.executable));
616 m_consoleStub.reset(new Utils::ConsoleProcess);
617 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
618 connect(m_consoleStub.data(), SIGNAL(processError(QString)),
619 SLOT(consoleStubError(QString)));
620 connect(m_consoleStub.data(), SIGNAL(processStarted()),
621 SLOT(consoleStubProcessStarted()));
622 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
623 SLOT(consoleStubExited()));
624 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
625 if (sp.environment.size())
626 m_consoleStub->setEnvironment(sp.environment);
627 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
628 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
634 void CdbEngine::consoleStubError(const QString &msg)
637 qDebug("consoleStubProcessMessage() in %s %s", stateName(state()), qPrintable(msg));
638 if (state() == EngineSetupRequested) {
639 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
640 notifyEngineSetupFailed();
642 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
645 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
648 void CdbEngine::consoleStubProcessStarted()
651 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
652 // Attach to console process.
653 DebuggerStartParameters attachParameters = startParameters();
654 attachParameters.executable.clear();
655 attachParameters.processArgs.clear();
656 attachParameters.attachPID = m_consoleStub->applicationPID();
657 attachParameters.startMode = AttachExternal;
658 attachParameters.useTerminal = false;
659 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
660 QString errorMessage;
661 if (!launchCDB(attachParameters, &errorMessage)) {
662 showMessage(errorMessage, LogError);
663 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
664 notifyEngineSetupFailed();
668 void CdbEngine::consoleStubExited()
672 void CdbEngine::setupEngine()
675 qDebug(">setupEngine");
676 // Nag to add symbol server
677 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
678 &(m_options->symbolPaths)))
679 m_options->toSettings(Core::ICore::instance()->settings());
682 if (!m_logTime.elapsed())
684 QString errorMessage;
685 // Console: Launch the stub with the suspended application and attach to it
686 // CDB in theory has a command line option '-2' that launches a
687 // console, too, but that immediately closes when the debuggee quits.
688 // Use the Creator stub instead.
689 const DebuggerStartParameters &sp = startParameters();
690 const bool launchConsole = isCreatorConsole(sp, *m_options);
691 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
692 const bool ok = launchConsole ?
693 startConsole(startParameters(), &errorMessage) :
694 launchCDB(startParameters(), &errorMessage);
696 qDebug("<setupEngine ok=%d", ok);
698 showMessage(errorMessage, LogError);
699 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
700 notifyEngineSetupFailed();
704 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
707 qDebug("launchCDB startMode=%d", sp.startMode);
708 const QChar blank(QLatin1Char(' '));
709 // Start engine which will run until initial breakpoint:
710 // Determine binary (force MSVC), extension lib name and path to use
711 // The extension is passed as relative name with the path variable set
712 //(does not work with absolute path names)
713 const QString executable = cdbBinary(sp);
714 if (executable.isEmpty()) {
715 *errorMessage = tr("There is no CDB executable specified.");
721 Utils::winIs64BitBinary(executable);
725 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
726 if (!extensionFi.isFile()) {
727 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
728 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
731 const QString extensionFileName = extensionFi.fileName();
733 QStringList arguments;
734 const bool isRemote = sp.startMode == AttachToRemote;
735 if (isRemote) { // Must be first
736 arguments << QLatin1String("-remote") << sp.remoteChannel;
738 arguments << (QLatin1String("-a") + extensionFileName);
740 // Source line info/No terminal breakpoint / Pull extension
741 arguments << QLatin1String("-lines") << QLatin1String("-G")
742 // register idle (debuggee stop) notification
743 << QLatin1String("-c")
744 << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
745 if (sp.useTerminal) // Separate console
746 arguments << QLatin1String("-2");
747 if (!m_options->symbolPaths.isEmpty())
748 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
749 if (!m_options->sourcePaths.isEmpty())
750 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
751 // Compile argument string preserving quotes
752 QString nativeArguments = m_options->additionalArguments;
753 switch (sp.startMode) {
756 if (!nativeArguments.isEmpty())
757 nativeArguments.push_back(blank);
758 nativeArguments += QDir::toNativeSeparators(sp.executable);
763 case AttachCrashedExternal:
764 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
765 if (sp.startMode == AttachCrashedExternal) {
766 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
768 if (isCreatorConsole(startParameters(), *m_options))
769 arguments << QLatin1String("-pr") << QLatin1String("-pb");
773 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
776 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
777 if (!nativeArguments.isEmpty())
778 nativeArguments.push_back(blank);
779 nativeArguments += sp.processArgs;
782 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
783 arg(QDir::toNativeSeparators(executable),
784 arguments.join(QString(blank)) + blank + nativeArguments,
785 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
786 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
787 showMessage(msg, LogMisc);
789 m_outputBuffer.clear();
790 const QStringList environment = sp.environment.size() == 0 ?
791 QProcessEnvironment::systemEnvironment().toStringList() :
792 sp.environment.toStringList();
793 m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
794 if (!sp.workingDirectory.isEmpty())
795 m_process.setWorkingDirectory(sp.workingDirectory);
798 if (!nativeArguments.isEmpty()) // Appends
799 m_process.setNativeArguments(nativeArguments);
801 m_process.start(executable, arguments);
802 if (!m_process.waitForStarted()) {
803 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
804 arg(QDir::toNativeSeparators(executable), m_process.errorString());
808 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
810 const unsigned long pid = 0;
812 showMessage(QString::fromLatin1("%1 running as %2").
813 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
814 m_hasDebuggee = true;
815 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
817 const QByteArray loadCommand = QByteArray(".load ")
818 + extensionFileName.toLocal8Bit();
819 postCommand(loadCommand, 0);
820 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
821 notifyEngineSetupOk();
826 void CdbEngine::setupInferior()
829 qDebug("setupInferior");
830 attemptBreakpointSynchronization();
832 if (startParameters().breakOnMain) {
833 const BreakpointParameters bp(BreakpointAtMain);
834 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings,
835 BreakpointModelId(-1), true), 0);
837 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
838 postCommand(".asm source_line", 0); // Source line in assembly
839 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
842 void CdbEngine::runEngine()
846 foreach (const QString &breakEvent, m_options->breakEvents)
847 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
851 bool CdbEngine::commandsPending() const
853 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
856 void CdbEngine::shutdownInferior()
859 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
860 isCdbProcessRunning());
862 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
864 qDebug("notifyInferiorShutdownOk");
865 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
866 notifyInferiorShutdownOk();
870 if (m_accessible) { // except console.
871 if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
873 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
874 notifyInferiorShutdownOk();
876 // A command got stuck.
877 if (commandsPending()) {
878 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
879 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
880 notifyInferiorShutdownFailed();
883 if (!canInterruptInferior()) {
884 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
885 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
886 notifyInferiorShutdownFailed();
889 interruptInferior(); // Calls us again
893 /* shutdownEngine/processFinished:
894 * Note that in the case of launching a process by the debugger, the debugger
895 * automatically quits a short time after reporting the session becoming
896 * inaccessible without debuggee (notifyInferiorExited). In that case,
897 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
898 * as not to confuse the state engine.
901 void CdbEngine::shutdownEngine()
904 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
905 "accessible=%d,commands pending=%d",
906 stateName(state()), isCdbProcessRunning(), m_accessible,
909 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
910 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
911 notifyEngineShutdownOk();
915 // No longer trigger anything from messages
916 m_ignoreCdbOutput = true;
917 // Go for kill if there are commands pending.
918 if (m_accessible && !commandsPending()) {
919 // detach (except console): Wait for debugger to finish.
920 if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
922 // Remote requires a bit more force to quit.
923 if (m_effectiveStartMode == AttachToRemote) {
924 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
925 postCommand("qq", 0);
929 m_notifyEngineShutdownOnTermination = true;
932 // Remote process. No can do, currently
933 m_notifyEngineShutdownOnTermination = true;
934 Utils::SynchronousProcess::stopProcess(m_process);
937 // Lost debuggee, debugger should quit anytime now
938 if (!m_hasDebuggee) {
939 m_notifyEngineShutdownOnTermination = true;
945 void CdbEngine::processFinished()
948 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
949 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
950 m_process.exitStatus(), m_process.exitCode());
952 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
954 showMessage(tr("CDB crashed"), LogError); // not in your life.
956 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
959 if (m_notifyEngineShutdownOnTermination) {
962 qDebug("notifyEngineIll");
963 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
966 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
967 notifyEngineShutdownOk();
970 // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
971 // Otherwise, we take a shortcut.
972 if (isSlaveEngine()) {
973 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
974 notifyInferiorExited();
976 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
977 notifyEngineSpontaneousShutdown();
982 void CdbEngine::detachDebugger()
984 postCommand(".detach", 0);
987 static inline bool isWatchIName(const QByteArray &iname)
989 return iname.startsWith("watch");
992 void CdbEngine::updateWatchData(const WatchData &dataIn,
993 const WatchUpdateFlags & flags)
995 if (debug || debugLocals || debugWatches)
996 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
997 elapsedLogTime(), m_accessible, stateName(state()),
998 flags.tryIncremental,
999 qPrintable(dataIn.toString()));
1001 if (!m_accessible) // Add watch data while running?
1005 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
1007 ByteArrayInputStream str(args);
1008 str << dataIn.iname << " \"" << dataIn.exp << '"';
1009 postExtensionCommand("addwatch", args, 0,
1010 &CdbEngine::handleAddWatch, 0,
1011 qVariantFromValue(dataIn));
1015 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
1016 WatchData data = dataIn;
1017 data.setAllUnneeded();
1018 watchHandler()->insertData(data);
1021 updateLocalVariable(dataIn.iname);
1024 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1026 WatchData item = qvariant_cast<WatchData>(reply->cookie);
1028 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
1029 if (reply->success) {
1030 updateLocalVariable(item.iname);
1032 item.setError(tr("Unable to add expression"));
1033 watchHandler()->insertData(item);
1034 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1035 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1036 reply->errorMessage), LogError);
1040 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1042 if (debuggerCore()->boolSetting(VerboseLog))
1043 str << blankSeparator << "-v";
1044 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1045 str << blankSeparator << "-c";
1046 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1047 if (!typeFormats.isEmpty())
1048 str << blankSeparator << "-T " << typeFormats;
1049 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1050 if (!individualFormats.isEmpty())
1051 str << blankSeparator << "-I " << individualFormats;
1054 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1056 const bool isWatch = isWatchIName(iname);
1058 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1059 QByteArray localsArguments;
1060 ByteArrayInputStream str(localsArguments);
1061 addLocalsOptions(str);
1063 const int stackFrame = stackHandler()->currentIndex();
1064 if (stackFrame < 0) {
1065 qWarning("Internal error; no stack frame in updateLocalVariable");
1068 str << blankSeparator << stackFrame;
1070 str << blankSeparator << iname;
1071 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1074 unsigned CdbEngine::debuggerCapabilities() const
1076 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1077 |WatchpointByAddressCapability|JumpToLineCapability|AddWatcherCapability
1078 |ReloadModuleCapability
1079 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1080 |BreakConditionCapability|TracePointCapability
1081 |BreakModuleCapability
1082 |OperateByInstructionCapability
1083 |RunToLineCapability;
1086 void CdbEngine::executeStep()
1088 if (!m_operateByInstruction)
1089 m_sourceStepInto = true; // See explanation at handleStackTrace().
1090 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1091 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1092 notifyInferiorRunRequested();
1095 void CdbEngine::executeStepOut()
1097 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1098 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1099 notifyInferiorRunRequested();
1102 void CdbEngine::executeNext()
1104 postCommand(QByteArray("p"), 0); // Step over -> p
1105 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1106 notifyInferiorRunRequested();
1109 void CdbEngine::executeStepI()
1114 void CdbEngine::executeNextI()
1119 void CdbEngine::continueInferior()
1121 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1122 notifyInferiorRunRequested();
1123 doContinueInferior();
1126 void CdbEngine::doContinueInferior()
1128 postCommand(QByteArray("g"), 0);
1131 bool CdbEngine::canInterruptInferior() const
1133 return m_effectiveStartMode != AttachToRemote && inferiorPid();
1136 void CdbEngine::interruptInferior()
1139 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1140 if (canInterruptInferior()) {
1141 doInterruptInferior(NoSpecialStop);
1143 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1144 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1145 notifyInferiorStopOk();
1146 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1147 notifyInferiorRunRequested();
1148 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1149 notifyInferiorRunOk();
1153 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1155 if (m_specialStopMode == NoSpecialStop)
1156 doInterruptInferior(CustomSpecialStop);
1157 m_customSpecialStopData.push_back(v);
1160 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1163 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1164 m_specialStopMode = sm;
1165 QString errorMessage;
1166 showMessage(QString::fromLatin1("Interrupting process %1...").arg(inferiorPid()), LogMisc);
1167 if (!winDebugBreakProcess(inferiorPid(), &errorMessage)) {
1168 m_specialStopMode = oldSpecialMode;
1169 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(inferiorPid()).arg(errorMessage), LogError);
1176 void CdbEngine::executeRunToLine(const ContextData &data)
1178 // Add one-shot breakpoint
1179 BreakpointParameters bp;
1181 bp.type =BreakpointByAddress;
1182 bp.address = data.address;
1184 bp.type =BreakpointByFileAndLine;
1185 bp.fileName = data.fileName;
1186 bp.lineNumber = data.lineNumber;
1188 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(-1), true), 0);
1192 void CdbEngine::executeRunToFunction(const QString &functionName)
1194 // Add one-shot breakpoint
1195 BreakpointParameters bp(BreakpointByFunction);
1196 bp.functionName = functionName;
1198 postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(-1), true), 0);
1202 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1204 const Registers registers = registerHandler()->registers();
1205 QTC_ASSERT(regnr < registers.size(), return)
1206 // Value is decimal or 0x-hex-prefixed
1208 ByteArrayInputStream str(cmd);
1209 str << "r " << registers.at(regnr).name << '=' << value;
1210 postCommand(cmd, 0);
1214 void CdbEngine::executeJumpToLine(const ContextData &data)
1217 // Goto address directly.
1218 jumpToAddress(data.address);
1219 gotoLocation(Location(data.address));
1221 // Jump to source line: Resolve source line address and go to that location
1223 ByteArrayInputStream str(cmd);
1224 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1225 const QVariant cookie = qVariantFromValue(data);
1226 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1230 void CdbEngine::jumpToAddress(quint64 address)
1232 // Fake a jump to address by setting the PC register.
1233 QByteArray registerCmd;
1234 ByteArrayInputStream str(registerCmd);
1235 // PC-register depending on 64/32bit.
1236 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1237 str.setHexPrefix(true);
1238 str.setIntegerBase(16);
1240 postCommand(registerCmd, 0);
1243 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1245 if (cmd->reply.isEmpty())
1247 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1248 // Set register 'rip' to hex address and goto lcoation
1249 QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1250 const int equalPos = answer.indexOf(" = ");
1253 answer.remove(0, equalPos + 3);
1254 answer.remove(QLatin1Char('`'));
1256 const quint64 address = answer.toLongLong(&ok, 16);
1257 if (ok && address) {
1258 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1259 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1260 jumpToAddress(address);
1261 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1265 static inline bool isAsciiWord(const QString &s)
1267 foreach (const QChar &c, s) {
1268 if (!c.isLetterOrNumber() || c.toAscii() == 0)
1274 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1277 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1279 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1280 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1284 ByteArrayInputStream str(cmd);
1285 switch (value.type()) {
1286 case QVariant::String: {
1287 // Convert qstring to Utf16 data not considering endianness for Windows.
1288 const QString s = value.toString();
1289 if (isAsciiWord(s)) {
1290 str << m_extensionCommandPrefixBA << "assign \"" << w->iname << '='
1291 << s.toLatin1() << '"';
1293 const QByteArray utf16(reinterpret_cast<const char *>(s.utf16()), 2 * s.size());
1294 str << m_extensionCommandPrefixBA << "assign -u " << w->iname << '='
1300 str << m_extensionCommandPrefixBA << "assign " << w->iname << '='
1301 << value.toString();
1305 postCommand(cmd, 0);
1306 // Update all locals in case we change a union or something pointed to
1307 // that affects other variables, too.
1311 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1313 int currentThreadId;
1314 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1315 threadsHandler()->setThreads(threads);
1316 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1317 forceCurrentThreadId : currentThreadId);
1320 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1323 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1324 if (reply->success) {
1326 data.fromString(reply->reply);
1328 // Continue sequence
1329 postCommandSequence(reply->commandSequence);
1331 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1335 void CdbEngine::executeDebuggerCommand(const QString &command)
1337 postCommand(command.toLocal8Bit(), QuietCommand);
1340 // Post command without callback
1341 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1344 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1345 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1346 if (!(flags & QuietCommand))
1347 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1348 m_process.write(cmd + '\n');
1351 // Post a built-in-command producing free-format output with a callback.
1352 // In order to catch the output, it is enclosed in 'echo' commands
1353 // printing a specially formatted token to be identifiable in the output.
1354 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1355 BuiltinCommandHandler handler,
1356 unsigned nextCommandFlag,
1357 const QVariant &cookie)
1359 if (!m_accessible) {
1360 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1361 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1362 showMessage(msg, LogError);
1365 if (!flags & QuietCommand)
1366 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1368 const int token = m_nextCommandToken++;
1369 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1371 m_builtinCommandQueue.push_back(pendingCommand);
1372 // Enclose command in echo-commands for token
1374 ByteArrayInputStream str(fullCmd);
1375 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1376 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1378 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1379 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1380 m_builtinCommandQueue.size(), nextCommandFlag);
1382 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1383 fullCmd.constData());
1384 m_process.write(fullCmd);
1387 // Post an extension command producing one-line output with a callback,
1388 // pass along token for identification in queue.
1389 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1390 const QByteArray &arguments,
1392 ExtensionCommandHandler handler,
1393 unsigned nextCommandFlag,
1394 const QVariant &cookie)
1396 if (!m_accessible) {
1397 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1398 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1399 showMessage(msg, LogError);
1403 const int token = m_nextCommandToken++;
1405 // Format full command with token to be recognizeable in the output
1407 ByteArrayInputStream str(fullCmd);
1408 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1409 if (!arguments.isEmpty())
1410 str << ' ' << arguments;
1412 if (!flags & QuietCommand)
1413 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1415 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1417 m_extensionCommandQueue.push_back(pendingCommand);
1418 // Enclose command in echo-commands for token
1420 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1421 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1422 m_extensionCommandQueue.size(), nextCommandFlag);
1423 m_process.write(fullCmd + '\n');
1426 void CdbEngine::activateFrame(int index)
1428 // TODO: assembler,etc
1431 const StackFrames &frames = stackHandler()->frames();
1432 QTC_ASSERT(index < frames.size(), return; )
1434 const StackFrame frame = frames.at(index);
1435 if (debug || debugLocals)
1436 qDebug("activateFrame idx=%d '%s' %d", index,
1437 qPrintable(frame.file), frame.line);
1438 stackHandler()->setCurrentIndex(index);
1439 const bool showAssembler = !frames.at(index).isUsable();
1440 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1441 watchHandler()->beginCycle();
1442 watchHandler()->endCycle();
1443 QAction *assemblerAction = theAssemblerAction();
1444 if (assemblerAction->isChecked()) {
1445 gotoLocation(frame);
1447 assemblerAction->trigger(); // Seems to trigger update
1450 gotoLocation(frame);
1455 void CdbEngine::updateLocals(bool forNewStackFrame)
1457 typedef QHash<QByteArray, int> WatcherHash;
1459 const int frameIndex = stackHandler()->currentIndex();
1460 if (frameIndex < 0) {
1461 watchHandler()->beginCycle();
1462 watchHandler()->endCycle();
1465 const StackFrame frame = stackHandler()->currentFrame();
1466 if (!frame.isUsable()) {
1467 watchHandler()->beginCycle();
1468 watchHandler()->endCycle();
1471 /* Watchers: Forcibly discard old symbol group as switching from
1472 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1473 * and cause errors as it seems to go 'stale' when switching threads.
1474 * Initial expand, get uninitialized and query */
1475 QByteArray arguments;
1476 ByteArrayInputStream str(arguments);
1479 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1480 if (!expanded.isEmpty()) {
1481 str << blankSeparator << "-e ";
1483 foreach(const QByteArray &e, expanded) {
1489 addLocalsOptions(str);
1490 // Uninitialized variables if desired. Quote as safeguard against shadowed
1491 // variables in case of errors in uninitializedVariables().
1492 if (debuggerCore()->boolSetting(UseCodeModel)) {
1493 QStringList uninitializedVariables;
1494 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1495 frame.function, frame.file, frame.line, &uninitializedVariables);
1496 if (!uninitializedVariables.isEmpty()) {
1497 str << blankSeparator << "-u \"";
1499 foreach(const QString &u, uninitializedVariables) {
1502 str << localsPrefixC << u;
1507 // Perform watches synchronization
1508 str << blankSeparator << "-W";
1509 const WatcherHash watcherHash = WatchHandler::watcherNames();
1510 if (!watcherHash.isEmpty()) {
1511 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1512 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1513 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1517 // Required arguments: frame
1518 str << blankSeparator << frameIndex;
1519 watchHandler()->beginCycle();
1520 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1523 void CdbEngine::selectThread(int index)
1525 if (index < 0 || index == threadsHandler()->currentThread())
1529 const int newThreadId = threadsHandler()->threads().at(index).id;
1530 threadsHandler()->setCurrentThread(index);
1532 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1533 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1536 // Default address range for showing disassembly.
1537 enum { DisassemblerRange = 512 };
1539 /* Try to emulate gdb's behaviour: When passed an address, display
1540 * the disassembled function. CDB's 'u' (disassemble) command takes a symbol,
1541 * but does not display the whole function, only 10 lines per default.
1542 * So, to ensure the agent's
1543 * address is in that range, resolve the function symbol, cache it and
1544 * request the disassembly for a range that contains the agent's address. */
1546 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1548 QTC_ASSERT(m_accessible, return;)
1549 const QString function = agent->location().functionName();
1550 const QString module = agent->location().from();
1551 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1552 if (function.isEmpty() || module.isEmpty()) {
1553 // No function, display a default range.
1554 postDisassemblerCommand(agent->address(), cookie);
1556 postResolveSymbol(module, function, cookie);
1560 void CdbEngine::postDisassemblerCommand(quint64 address, const QVariant &cookie)
1562 postDisassemblerCommand(address - DisassemblerRange / 2,
1563 address + DisassemblerRange / 2, cookie);
1566 void CdbEngine::postDisassemblerCommand(quint64 address, quint64 endAddress,
1567 const QVariant &cookie)
1570 ByteArrayInputStream str(cmd);
1571 str << "u " << hex <<hexPrefixOn << address << ' ' << endAddress;
1572 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1575 void CdbEngine::postResolveSymbol(const QString &module, const QString &function,
1576 const QVariant &cookie)
1578 const QString symbol = module + QLatin1Char('!') + function;
1579 const QList<quint64> addresses = m_symbolAddressCache.values(symbol);
1580 if (addresses.isEmpty()) {
1581 QVariantList cookieList;
1582 cookieList << QVariant(symbol) << cookie;
1583 showMessage(QLatin1String("Resolving symbol: ") + symbol, LogMisc);
1584 postBuiltinCommand(QByteArray("x ") + symbol.toLatin1(), 0,
1585 &CdbEngine::handleResolveSymbol, 0,
1586 QVariant(cookieList));
1588 showMessage(QString::fromLatin1("Using cached addresses for %1.").
1589 arg(symbol), LogMisc);
1590 handleResolveSymbol(addresses, cookie);
1594 // Parse address from 'x' response.
1595 // "00000001`3f7ebe80 module!foo (void)"
1596 static inline quint64 resolvedAddress(const QByteArray &line)
1598 const int blankPos = line.indexOf(' ');
1599 if (blankPos >= 0) {
1600 QByteArray addressBA = line.left(blankPos);
1601 if (addressBA.size() > 9 && addressBA.at(8) == '`')
1602 addressBA.remove(8, 1);
1604 const quint64 address = addressBA.toULongLong(&ok, 16);
1611 void CdbEngine::handleResolveSymbol(const CdbBuiltinCommandPtr &command)
1613 QTC_ASSERT(command->cookie.type() == QVariant::List, return; );
1614 const QVariantList cookieList = command->cookie.toList();
1615 const QString symbol = cookieList.front().toString();
1616 // Insert all matches of (potentially) ambiguous symbols
1617 if (const int size = command->reply.size()) {
1618 for (int i = 0; i < size; i++) {
1619 if (const quint64 address = resolvedAddress(command->reply.at(i))) {
1620 m_symbolAddressCache.insert(symbol, address);
1621 showMessage(QString::fromLatin1("Obtained 0x%1 for %2 (#%3)").
1622 arg(address, 0, 16).arg(symbol).arg(i + 1), LogMisc);
1626 showMessage(QLatin1String("Symbol resolution failed: ")
1627 + QString::fromLatin1(command->joinedReply()),
1630 handleResolveSymbol(m_symbolAddressCache.values(symbol), cookieList.back());
1633 // Find the function address matching needle in a list of function
1634 // addresses obtained from the 'x' command. Check for the
1635 // mimimum POSITIVE offset (needle >= function address.)
1636 static inline quint64 findClosestFunctionAddress(const QList<quint64> &addresses,
1639 const int size = addresses.size();
1643 return addresses.front();
1644 int closestIndex = 0;
1645 quint64 closestOffset = 0xFFFFFFFF;
1646 for (int i = 0; i < size; i++) {
1647 if (addresses.at(i) <= needle) {
1648 const quint64 offset = needle - addresses.at(i);
1649 if (offset < offset) {
1650 closestOffset = offset;
1655 return addresses.at(closestIndex);
1658 void CdbEngine::handleResolveSymbol(const QList<quint64> &addresses, const QVariant &cookie)
1660 // Disassembly mode: Determine suitable range containing the
1661 // agent's address within the function to display.
1662 if (qVariantCanConvert<DisassemblerAgent*>(cookie)) {
1663 DisassemblerAgent *agent = cookie.value<DisassemblerAgent *>();
1664 const quint64 agentAddress = agent->address();
1665 const quint64 functionAddress
1666 = findClosestFunctionAddress(addresses, agentAddress);
1667 if (functionAddress > 0 && functionAddress <= agentAddress) {
1668 quint64 endAddress = agentAddress + DisassemblerRange / 2;
1669 if (const quint64 remainder = endAddress % 8)
1670 endAddress += 8 - remainder;
1671 postDisassemblerCommand(functionAddress, endAddress, cookie);
1673 postDisassemblerCommand(agentAddress, cookie);
1679 // Parse: "00000000`77606060 cc int 3"
1680 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1682 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1683 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1684 agent->setContents(parseCdbDisassembler(command->reply));
1687 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1690 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1691 const MemoryViewCookie cookie(agent, editor, addr, length);
1693 postFetchMemory(cookie);
1695 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1699 void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
1702 ByteArrayInputStream str(args);
1703 str << cookie.address << ' ' << cookie.length;
1704 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0,
1705 qVariantFromValue(cookie));
1708 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1710 QTC_ASSERT(!data.isEmpty(), return; )
1711 if (!m_accessible) {
1712 const MemoryChangeCookie cookie(addr, data);
1713 doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1715 postCommand(cdbWriteMemoryCommand(addr, data), 0);
1719 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1721 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1722 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1723 if (command->success) {
1724 const QByteArray data = QByteArray::fromBase64(command->reply);
1725 if (unsigned(data.size()) == memViewCookie.length)
1726 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1727 memViewCookie.address, data);
1729 showMessage(QString::fromLocal8Bit(command->errorMessage), LogWarning);
1733 void CdbEngine::reloadModules()
1735 postCommandSequence(CommandListModules);
1738 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1742 void CdbEngine::loadAllSymbols()
1746 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1748 Q_UNUSED(moduleName)
1751 void CdbEngine::reloadRegisters()
1753 postCommandSequence(CommandListRegisters);
1756 void CdbEngine::reloadSourceFiles()
1760 void CdbEngine::reloadFullStack()
1763 qDebug("%s", Q_FUNC_INFO);
1764 postCommandSequence(CommandListStack);
1767 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1769 if (reply->success) {
1770 notifyInferiorPid(reply->reply.toULongLong());
1771 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1772 notifyInferiorSetupOk();
1774 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1775 arg(QLatin1String(reply->errorMessage)), LogError);
1776 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1777 notifyInferiorSetupFailed();
1781 // Parse CDB gdbmi register syntax
1782 static inline Register parseRegister(const GdbMi &gdbmiReg)
1785 reg.name = gdbmiReg.findChild("name").data();
1786 const GdbMi description = gdbmiReg.findChild("description");
1787 if (description.type() != GdbMi::Invalid) {
1789 reg.name += description.data();
1792 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1796 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1798 if (reply->success) {
1800 value.fromString(reply->reply);
1801 if (value.type() == GdbMi::List) {
1803 modules.reserve(value.childCount());
1804 foreach (const GdbMi &gdbmiModule, value.children()) {
1806 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1807 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1808 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1809 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1810 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1811 module.symbolsRead = Module::ReadOk;
1812 modules.push_back(module);
1814 modulesHandler()->setModules(modules);
1816 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1817 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1820 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1821 arg(QLatin1String(reply->errorMessage)), LogError);
1823 postCommandSequence(reply->commandSequence);
1827 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1829 if (reply->success) {
1831 value.fromString(reply->reply);
1832 if (value.type() == GdbMi::List) {
1833 Registers registers;
1834 registers.reserve(value.childCount());
1835 foreach (const GdbMi &gdbmiReg, value.children())
1836 registers.push_back(parseRegister(gdbmiReg));
1837 registerHandler()->setAndMarkRegisters(registers);
1839 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1840 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1843 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1844 arg(QLatin1String(reply->errorMessage)), LogError);
1846 postCommandSequence(reply->commandSequence);
1849 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1851 if (reply->success) {
1852 QList<WatchData> watchData;
1854 root.fromString(reply->reply);
1855 QTC_ASSERT(root.isList(), return ; )
1857 qDebug() << root.toString(true, 4);
1859 // Courtesy of GDB engine
1860 foreach (const GdbMi &child, root.children()) {
1862 dummy.iname = child.findChild("iname").data();
1863 dummy.name = QLatin1String(child.findChild("name").data());
1864 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1866 watchHandler()->insertBulkData(watchData);
1867 watchHandler()->endCycle();
1869 QDebug nsp = qDebug().nospace();
1870 nsp << "Obtained " << watchData.size() << " items:\n";
1871 foreach (const WatchData &wd, watchData)
1872 nsp << wd.toString() <<'\n';
1874 const bool forNewStackFrame = reply->cookie.toBool();
1875 if (forNewStackFrame)
1876 emit stackFrameCompleted();
1878 showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1882 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1884 if (!reply->success)
1885 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1888 enum CdbExecutionStatus {
1889 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1890 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1891 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1892 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1893 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1894 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1895 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1897 static const char *cdbStatusName(unsigned long s)
1900 case CDB_STATUS_NO_CHANGE:
1904 case CDB_STATUS_GO_HANDLED:
1905 return "go_handled";
1906 case CDB_STATUS_GO_NOT_HANDLED:
1907 return "go_not_handled";
1908 case CDB_STATUS_STEP_OVER:
1910 case CDB_STATUS_STEP_INTO:
1912 case CDB_STATUS_BREAK:
1914 case CDB_STATUS_NO_DEBUGGEE:
1915 return "no_debuggee";
1916 case CDB_STATUS_STEP_BRANCH:
1917 return "step_branch";
1918 case CDB_STATUS_IGNORE_EVENT:
1919 return "ignore_event";
1920 case CDB_STATUS_RESTART_REQUESTED:
1921 return "restart_requested";
1922 case CDB_STATUS_REVERSE_GO:
1923 return "reverse_go";
1924 case CDB_STATUS_REVERSE_STEP_BRANCH:
1925 return "reverse_step_branch";
1926 case CDB_STATUS_REVERSE_STEP_OVER:
1927 return "reverse_step_over";
1928 case CDB_STATUS_REVERSE_STEP_INTO:
1929 return "reverse_step_into";
1934 /* Examine how to react to a stop. */
1935 enum StopActionFlags
1938 StopReportLog = 0x1,
1939 StopReportStatusMessage = 0x2,
1940 StopReportParseError = 0x4,
1941 StopShowExceptionMessageBox = 0x8,
1942 // Notify stop or just continue
1943 StopNotifyStop = 0x10,
1944 StopIgnoreContinue = 0x20,
1945 // Hit on break in artificial stop thread (created by DebugBreak()).
1946 StopInArtificialThread = 0x40,
1947 StopShutdownInProgress = 0x80 // Shutdown in progress
1950 static inline QString msgTracePointTriggered(BreakpointModelId id, const int number,
1951 const QString &threadId)
1953 return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1954 .arg(id.toString()).arg(number).arg(threadId);
1957 static inline QString msgCheckingConditionalBreakPoint(BreakpointModelId id, const int number,
1958 const QByteArray &condition,
1959 const QString &threadId)
1961 return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1962 .arg(id.toString()).arg(number).arg(threadId, QString::fromAscii(condition));
1965 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1967 QString *exceptionBoxMessage,
1968 bool conditionalBreakPointTriggered)
1970 // Report stop reason (GDBMI)
1972 if (targetState() == DebuggerFinished)
1973 rc |= StopShutdownInProgress;
1975 qDebug("%s", stopReason.toString(true, 4).constData());
1976 const QByteArray reason = stopReason.findChild("reason").data();
1977 if (reason.isEmpty()) {
1978 *message = tr("Malformed stop response received.");
1979 rc |= StopReportParseError|StopNotifyStop;
1982 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1983 if (state() == InferiorStopOk) {
1984 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1985 arg(QString::fromAscii(reason));
1986 rc |= StopReportLog;
1989 const int threadId = stopReason.findChild("threadId").data().toInt();
1990 if (reason == "breakpoint") {
1991 // Note: Internal breakpoints (run to line) are reported with id=0.
1992 // Step out creates temporary breakpoints with id 10000.
1993 BreakpointModelId id;
1995 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1996 if (breakpointIdG.isValid()) {
1997 id = BreakpointModelId(breakpointIdG.data().toInt());
1998 if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1999 const BreakpointResponse parameters = breakHandler()->response(id);
2000 if (!parameters.message.isEmpty()) {
2001 showMessage(parameters.message + QLatin1Char('\n'), AppOutput);
2002 showMessage(parameters.message, LogMisc);
2004 // Trace point? Just report.
2005 number = parameters.id.majorPart();
2006 if (parameters.tracepoint) {
2007 *message = msgTracePointTriggered(id, number, QString::number(threadId));
2008 return StopReportLog|StopIgnoreContinue;
2010 // Trigger evaluation of BP expression unless we are already in the response.
2011 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
2012 *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
2013 QString::number(threadId));
2014 ConditionalBreakPointCookie cookie(id);
2015 cookie.stopReason = stopReason;
2016 evaluateExpression(parameters.condition, qVariantFromValue(cookie));
2017 return StopReportLog;
2020 id = BreakpointModelId();
2023 QString tid = QString::number(threadId);
2024 if (id && breakHandler()->type(id) == WatchpointAtAddress) {
2025 *message = msgWatchpointByAddressTriggered(id, number, breakHandler()->address(id), tid);
2026 } else if (id && breakHandler()->type(id) == WatchpointAtExpression) {
2027 *message = msgWatchpointByExpressionTriggered(id, number, breakHandler()->expression(id), tid);
2029 *message = msgBreakpointTriggered(id, number, tid);
2031 rc |= StopReportStatusMessage|StopNotifyStop;
2034 if (reason == "exception") {
2035 WinException exception;
2036 exception.fromGdbMI(stopReason);
2037 QString description = exception.toString();
2039 // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
2040 // pulls DLLs. Avoid showing a 'stopped' Message box.
2041 if (exception.exceptionCode == winExceptionStartupCompleteTrap
2042 || exception.exceptionCode == winExceptionWX86Breakpoint)
2043 return StopNotifyStop;
2044 if (exception.exceptionCode == winExceptionCtrlPressed) {
2045 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
2046 *message = msgInterrupted();
2047 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
2050 if (isDebuggerWinException(exception.exceptionCode)) {
2051 rc |= StopReportStatusMessage|StopNotifyStop;
2052 // Detect interruption by DebugBreak() and force a switch to thread 0.
2053 if (exception.function == "ntdll!DbgBreakPoint")
2054 rc |= StopInArtificialThread;
2055 *message = msgInterrupted();
2059 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
2060 *message = description;
2061 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
2064 *message = msgStopped(QLatin1String(reason));
2065 rc |= StopReportStatusMessage|StopNotifyStop;
2069 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
2075 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
2076 elapsedLogTime(), messageBA.constData(),
2077 stateName(state()), m_specialStopMode);
2079 // Switch source level debugging
2080 syncOperateByInstruction(m_operateByInstructionPending);
2082 // Engine-special stop reasons: Breakpoints and setup
2083 const SpecialStopMode specialStopMode = m_specialStopMode;
2085 m_specialStopMode = NoSpecialStop;
2087 switch(specialStopMode) {
2088 case SpecialStopSynchronizeBreakpoints:
2090 qDebug("attemptBreakpointSynchronization in special stop");
2091 attemptBreakpointSynchronization();
2092 doContinueInferior();
2094 case SpecialStopGetWidgetAt:
2095 postWidgetAtCommand();
2097 case CustomSpecialStop:
2098 foreach (const QVariant &data, m_customSpecialStopData)
2099 handleCustomSpecialStop(data);
2100 m_customSpecialStopData.clear();
2101 doContinueInferior();
2107 if (state() == EngineSetupRequested) { // Temporary stop at beginning
2108 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
2109 notifyEngineSetupOk();
2113 stopReason.fromString(messageBA);
2114 processStop(stopReason, false);
2117 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
2119 // Further examine stop and report to user
2121 QString exceptionBoxMessage;
2122 int forcedThreadId = -1;
2123 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
2124 conditionalBreakPointTriggered);
2125 // Do the non-blocking log reporting
2126 if (stopFlags & StopReportLog)
2127 showMessage(message, LogMisc);
2128 if (stopFlags & StopReportStatusMessage)
2129 showStatusMessage(message);
2130 if (stopFlags & StopReportParseError)
2131 showMessage(message, LogError);
2132 // Ignore things like WOW64, report tracepoints.
2133 if (stopFlags & StopIgnoreContinue) {
2134 postCommand("g", 0);
2137 // Notify about state and send off command sequence to get stack, etc.
2138 if (stopFlags & StopNotifyStop) {
2139 if (state() == InferiorStopRequested) {
2140 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
2141 notifyInferiorStopOk();
2143 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
2144 notifyInferiorSpontaneousStop();
2146 // Prevent further commands from being sent if shutdown is in progress
2147 if (stopFlags & StopShutdownInProgress) {
2148 showMessage(QString::fromLatin1("Shutdown request detected..."));
2151 const bool sourceStepInto = m_sourceStepInto;
2152 m_sourceStepInto = false;
2153 // Start sequence to get all relevant data.
2154 if (stopFlags & StopInArtificialThread) {
2155 showMessage(tr("Switching to main thread..."), LogMisc);
2156 postCommand("~0 s", 0);
2158 // Re-fetch stack again.
2159 postCommandSequence(CommandListStack);
2161 const GdbMi stack = stopReason.findChild("stack");
2162 if (stack.isValid()) {
2163 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
2164 executeStep(); // Hit on a frame while step into, see parseStackTrace().
2168 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
2171 const GdbMi threads = stopReason.findChild("threads");
2172 if (threads.isValid()) {
2173 parseThreads(threads, forcedThreadId);
2175 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
2177 // Fire off remaining commands asynchronously
2178 if (!m_pendingBreakpointMap.isEmpty())
2179 postCommandSequence(CommandListBreakPoints);
2180 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
2181 postCommandSequence(CommandListRegisters);
2182 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2183 postCommandSequence(CommandListModules);
2185 // After the sequence has been sent off and CDB is pondering the commands,
2186 // pop up a message box for exceptions.
2187 if (stopFlags & StopShowExceptionMessageBox)
2188 showStoppedByExceptionMessageBox(exceptionBoxMessage);
2191 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2193 const DebuggerState s = state();
2194 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2198 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2199 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2202 case EngineShutdownRequested:
2205 case InferiorShutdownRequested:
2213 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2215 const DebuggerState s = state();
2218 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2222 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2223 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2226 case EngineSetupRequested:
2228 case EngineRunRequested:
2229 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2230 notifyEngineRunAndInferiorRunOk();
2233 case InferiorStopOk:
2234 // Inaccessible without debuggee (exit breakpoint)
2235 // We go for spontaneous engine shutdown instead.
2236 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2238 qDebug("Lost debuggeee");
2239 m_hasDebuggee = false;
2242 case InferiorRunRequested:
2243 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2244 notifyInferiorRunOk();
2247 case EngineShutdownRequested:
2254 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2257 QDebug nospace = qDebug().nospace();
2258 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2259 << ' ' << stateName(state());
2260 if (t == 'N' || debug > 1) {
2261 nospace << ' ' << message;
2263 nospace << ' ' << message.size() << " bytes";
2267 // Is there a reply expected, some command queued?
2268 if (t == 'R' || t == 'N') {
2269 if (token == -1) { // Default token, user typed in extension command
2270 showMessage(QString::fromLatin1(message), LogMisc);
2273 const int index = indexOfCommand(m_extensionCommandQueue, token);
2275 // Did the command finish? Take off queue and complete, invoke CB
2276 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2278 command->success = true;
2279 command->reply = message;
2281 command->success = false;
2282 command->errorMessage = message;
2285 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2286 command->command.constData(), command->token, m_extensionCommandQueue.size());
2287 if (command->handler)
2288 (this->*(command->handler))(command);
2293 if (what == "debuggee_output") {
2294 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2298 if (what == "event") {
2299 showStatusMessage(QString::fromAscii(message), 5000);
2303 if (what == "session_accessible") {
2304 if (!m_accessible) {
2305 m_accessible = true;
2306 handleSessionAccessible(message.toULong());
2311 if (what == "session_inaccessible") {
2313 m_accessible = false;
2314 handleSessionInaccessible(message.toULong());
2319 if (what == "session_idle") {
2320 handleSessionIdle(message);
2324 if (what == "exception") {
2325 WinException exception;
2327 gdbmi.fromString(message);
2328 exception.fromGdbMI(gdbmi);
2329 const QString message = exception.toString(true);
2330 showStatusMessage(message);
2331 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2332 if (exception.exceptionCode == winExceptionCppException)
2333 showMessage(message + QLatin1Char('\n'), AppOutput);
2341 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2342 enum { CdbPromptLength = 7 };
2344 static inline bool isCdbPrompt(const QByteArray &c)
2346 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2347 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2348 && std::isdigit(c.at(4));
2351 // Check for '<token>32>' or '<token>32<'
2352 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2353 int *token, bool *isStart)
2357 const int tokenPrefixSize = tokenPrefix.size();
2358 const int size = c.size();
2359 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2361 switch (c.at(size - 1)) {
2371 if (!c.startsWith(tokenPrefix))
2374 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2378 void CdbEngine::parseOutputLine(QByteArray line)
2380 // The hooked output callback in the extension suppresses prompts,
2381 // it should happen only in initial and exit stages. Note however that
2382 // if the output is not hooked, sequences of prompts are possible which
2383 // can mix things up.
2384 while (isCdbPrompt(line))
2385 line.remove(0, CdbPromptLength);
2386 // An extension notification (potentially consisting of several chunks)
2387 if (line.startsWith(m_creatorExtPrefix)) {
2388 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2389 const char type = line.at(m_creatorExtPrefix.size());
2391 const int tokenPos = m_creatorExtPrefix.size() + 2;
2392 const int tokenEndPos = line.indexOf('|', tokenPos);
2393 QTC_ASSERT(tokenEndPos != -1, return)
2394 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2396 const int remainingChunksPos = tokenEndPos + 1;
2397 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2398 QTC_ASSERT(remainingChunksEndPos != -1, return)
2399 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2400 // const char 'serviceName'
2401 const int whatPos = remainingChunksEndPos + 1;
2402 const int whatEndPos = line.indexOf('|', whatPos);
2403 QTC_ASSERT(whatEndPos != -1, return)
2404 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2405 // Build up buffer, call handler once last chunk was encountered
2406 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2407 if (remainingChunks == 0) {
2408 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2409 m_extensionMessageBuffer.clear();
2413 // Check for command start/end tokens within which the builtin command
2414 // output is enclosed
2416 bool isStartToken = false;
2417 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2419 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2420 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2422 // If there is a current command, wait for end of output indicated by token,
2423 // command, trigger handler and finish, else append to its output.
2424 if (m_currentBuiltinCommandIndex != -1) {
2425 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2426 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2427 if (isCommandToken) {
2428 // Did the command finish? Invoke callback and remove from queue.
2430 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2431 currentCommand->command.constData(), currentCommand->token,
2432 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2433 QTC_ASSERT(token == currentCommand->token, return; );
2434 if (currentCommand->handler)
2435 (this->*(currentCommand->handler))(currentCommand);
2436 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2437 m_currentBuiltinCommandIndex = -1;
2439 // Record output of current command
2440 currentCommand->reply.push_back(line);
2443 } // m_currentCommandIndex
2444 if (isCommandToken) {
2445 // Beginning command token encountered, start to record output.
2446 const int index = indexOfCommand(m_builtinCommandQueue, token);
2447 QTC_ASSERT(isStartToken && index != -1, return; );
2448 m_currentBuiltinCommandIndex = index;
2449 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2451 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2455 showMessage(QString::fromLocal8Bit(line), LogMisc);
2458 void CdbEngine::readyReadStandardOut()
2460 if (m_ignoreCdbOutput)
2462 m_outputBuffer += m_process.readAllStandardOutput();
2463 // Split into lines and parse line by line.
2465 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2466 if (endOfLinePos == -1) {
2470 QByteArray line = m_outputBuffer.left(endOfLinePos);
2471 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2472 line.truncate(line.size() - 1);
2473 parseOutputLine(line);
2474 m_outputBuffer.remove(0, endOfLinePos + 1);
2479 void CdbEngine::readyReadStandardError()
2481 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2484 void CdbEngine::processError()
2486 showMessage(m_process.errorString(), LogError);
2490 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2491 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2493 QByteArray cmd(cmdC);
2494 ByteArrayInputStream str(cmd);
2495 foreach(const BreakpointData *bp, bps)
2496 str << ' ' << bp->bpNumber;
2501 bool CdbEngine::stateAcceptsBreakpointChanges() const
2505 case InferiorStopOk:
2513 bool CdbEngine::acceptsBreakpoint(BreakpointModelId id) const
2515 const BreakpointParameters &data = breakHandler()->breakpointData(id);
2516 if (!DebuggerEngine::isCppBreakpoint(data))
2518 switch (data.type) {
2520 case BreakpointAtFork:
2521 case WatchpointAtExpression:
2522 case BreakpointAtSysCall:
2524 case WatchpointAtAddress:
2525 case BreakpointByFileAndLine:
2526 case BreakpointByFunction:
2527 case BreakpointByAddress:
2528 case BreakpointAtThrow:
2529 case BreakpointAtCatch:
2530 case BreakpointAtMain:
2531 case BreakpointAtExec:
2537 // Context for fixing file/line-type breakpoints, for delayed creation.
2538 class BreakpointCorrectionContext
2541 explicit BreakpointCorrectionContext(const CPlusPlus::Snapshot &s,
2542 const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy) :
2543 m_snapshot(s), m_workingCopy(workingCopy) {}
2545 unsigned fixLineNumber(const QString &fileName, unsigned lineNumber) const;
2548 const CPlusPlus::Snapshot m_snapshot;
2549 CPlusPlus::CppModelManagerInterface::WorkingCopy m_workingCopy;
2552 static CPlusPlus::Document::Ptr getParsedDocument(const QString &fileName,
2553 const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy,
2554 const CPlusPlus::Snapshot &snapshot)
2557 if (workingCopy.contains(fileName)) {
2558 src = workingCopy.source(fileName);
2560 Utils::FileReader reader;
2561 if (reader.fetch(fileName)) // ### FIXME error reporting
2562 src = QString::fromLocal8Bit(reader.data()); // ### FIXME encoding
2565 QByteArray source = snapshot.preprocessedCode(src, fileName);
2567 CPlusPlus::Document::Ptr doc = snapshot.documentFromSource(source, fileName);
2572 unsigned BreakpointCorrectionContext::fixLineNumber(const QString &fileName,
2573 unsigned lineNumber) const
2575 CPlusPlus::Document::Ptr doc = m_snapshot.document(fileName);
2576 if (!doc || !doc->translationUnit()->ast())
2577 doc = getParsedDocument(fileName, m_workingCopy, m_snapshot);
2579 CPlusPlus::FindCdbBreakpoint findVisitor(doc->translationUnit());
2580 const unsigned correctedLine = findVisitor(lineNumber);
2581 if (!correctedLine) {
2582 qWarning("Unable to find breakpoint location for %s:%d",
2583 qPrintable(QDir::toNativeSeparators(fileName)), lineNumber);
2587 qDebug("Code model: Breakpoint line %u -> %u in %s",
2588 lineNumber, correctedLine, qPrintable(fileName));
2589 return correctedLine;
2592 void CdbEngine::attemptBreakpointSynchronization()
2597 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2598 // Check if there is anything to be done at all.
2599 BreakHandler *handler = breakHandler();
2600 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2601 foreach (BreakpointModelId id, handler->unclaimedBreakpointIds())
2602 if (acceptsBreakpoint(id))
2603 handler->setEngine(id, this);
2605 // Quick check: is there a need to change something? - Populate module cache
2606 bool changed = false;
2607 const BreakpointModelIds ids = handler->engineBreakpointIds(this);
2608 foreach (BreakpointModelId id, ids) {
2609 switch (handler->state(id)) {
2610 case BreakpointInsertRequested:
2611 case BreakpointRemoveRequested:
2612 case BreakpointChangeRequested:
2615 case BreakpointInserted: {
2616 // Collect the new modules matching the files.
2617 // In the future, that information should be obtained from the build system.
2618 const BreakpointParameters &data = handler->breakpointData(id);
2619 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2620 m_fileNameModuleHash.insert(data.fileName, data.module);
2628 if (debugBreakpoints)
2629 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2630 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2634 if (!m_accessible) {
2636 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2637 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2640 // Add/Change breakpoints and store pending ones in map, since
2641 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2642 // handleBreakPoints will the complete that information and set it on the break handler.
2643 bool addedChanged = false;
2644 QScopedPointer<BreakpointCorrectionContext> lineCorrection;
2645 foreach (BreakpointModelId id, ids) {
2646 BreakpointParameters parameters = handler->breakpointData(id);
2647 BreakpointResponse response;
2648 response.fromParameters(parameters);
2649 response.id = BreakpointResponseId(id.majorPart(), id.minorPart());
2650 // If we encountered that file and have a module for it: Add it.
2651 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2652 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2653 if (it != m_fileNameModuleHash.constEnd())
2654 parameters.module = it.value();
2656 switch (handler->state(id)) {
2657 case BreakpointInsertRequested:
2658 if (parameters.type == BreakpointByFileAndLine) {
2659 if (lineCorrection.isNull())
2660 lineCorrection.reset(new BreakpointCorrectionContext(debuggerCore()->cppCodeModelSnapshot(),
2661 CPlusPlus::CppModelManagerInterface::instance()->workingCopy()));
2662 response.lineNumber = lineCorrection->fixLineNumber(parameters.fileName, parameters.lineNumber);
2663 postCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false), 0);
2665 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2667 if (!parameters.enabled)
2668 postCommand("bd " + QByteArray::number(id.majorPart()), 0);
2669 handler->notifyBreakpointInsertProceeding(id);
2670 handler->notifyBreakpointInsertOk(id);
2671 m_pendingBreakpointMap.insert(id, response);
2672 addedChanged = true;
2673 // Ensure enabled/disabled is correct in handler and line number is there.
2674 handler->setResponse(id, response);
2675 if (debugBreakpoints)
2676 qDebug("Adding %d %s\n", id.toInternalId(),
2677 qPrintable(response.toString()));
2679 case BreakpointChangeRequested:
2680 handler->notifyBreakpointChangeProceeding(id);
2681 if (debugBreakpoints)
2682 qDebug("Changing %d:\n %s\nTo %s\n", id.toInternalId(),
2683 qPrintable(handler->response(id).toString()),
2684 qPrintable(parameters.toString()));
2685 if (parameters.enabled != handler->response(id).enabled) {
2686 // Change enabled/disabled breakpoints without triggering update.
2687 postCommand((parameters.enabled ? "be " : "bd ")
2688 + QByteArray::number(id.majorPart()), 0);
2689 response.pending = false;
2690 response.enabled = parameters.enabled;
2691 handler->setResponse(id, response);
2693 // Delete and re-add, triggering update
2694 addedChanged = true;
2695 postCommand("bc " + QByteArray::number(id.majorPart()), 0);
2696 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2697 m_pendingBreakpointMap.insert(id, response);
2699 handler->notifyBreakpointChangeOk(id);
2701 case BreakpointRemoveRequested:
2702 postCommand("bc " + QByteArray::number(id.majorPart()), 0);
2703 handler->notifyBreakpointRemoveProceeding(id);
2704 handler->notifyBreakpointRemoveOk(id);
2705 m_pendingBreakpointMap.remove(id);
2711 // List breakpoints and send responses
2713 postCommandSequence(CommandListBreakPoints);
2716 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2717 // manager to correctly process it) and convert to clean path.
2718 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2721 QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2722 if (it != m_normalizedFileCache.constEnd())
2724 if (debugSourceMapping)
2725 qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2726 // Do we have source path mappings? ->Apply.
2727 const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2729 // Up/lower case normalization according to Windows.
2731 QString normalized = Utils::normalizePathName(fileName);
2733 QString normalized = fileName;
2735 if (debugSourceMapping)
2736 qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2737 // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2738 const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2739 NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2741 // At least upper case drive letter if failed.
2742 if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2743 result.fileName[0] = result.fileName.at(0).toUpper();
2745 m_normalizedFileCache.insert(f, result);
2746 if (debugSourceMapping)
2747 qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2751 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2752 // has more processing.
2753 static StackFrames parseFrames(const GdbMi &gdbmi)
2756 const int count = gdbmi.childCount();
2758 for (int i = 0; i < count; i++) {
2759 const GdbMi &frameMi = gdbmi.childAt(i);
2762 const GdbMi fullName = frameMi.findChild("fullname");
2763 if (fullName.isValid()) {
2764 frame.file = QFile::decodeName(fullName.data());
2765 frame.line = frameMi.findChild("line").data().toInt();
2766 frame.usable = false; // To be decided after source path mapping.
2768 frame.function = QLatin1String(frameMi.findChild("func").data());
2769 frame.from = QLatin1String(frameMi.findChild("from").data());
2770 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2771 rc.push_back(frame);
2776 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2778 // Parse frames, find current. Special handling for step into:
2779 // When stepping into on an actual function (source mode) by executing 't', an assembler
2780 // frame pointing at the jmp instruction is hit (noticeable by top function being
2781 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2782 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2784 StackFrames frames = parseFrames(data);
2785 const int count = frames.size();
2786 for (int i = 0; i < count; i++) {
2787 const bool hasFile = !frames.at(i).file.isEmpty();
2788 // jmp-frame hit by step into, do another 't' and abort sequence.
2789 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2790 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2791 return ParseStackStepInto;
2794 const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2795 frames[i].file = fileName.fileName;
2796 frames[i].usable = fileName.exists;
2797 if (current == -1 && frames[i].usable)
2801 if (count && current == -1) // No usable frame, use assembly.
2804 stackHandler()->setFrames(frames);
2805 activateFrame(current);
2809 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2811 if (command->success) {
2813 data.fromString(command->reply);
2814 parseStackTrace(data, false);
2815 postCommandSequence(command->commandSequence);
2817 showMessage(command->errorMessage, LogError);
2821 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2824 if (command->success) {
2825 value = command->reply.toInt();
2827 showMessage(command->errorMessage, LogError);
2829 // Is this a conditional breakpoint?
2830 if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2831 const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2832 const QString message = value ?
2833 tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2834 arg(value).arg(cookie.id.toString()) :
2835 tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2836 arg(cookie.id.toString());
2837 showMessage(message, LogMisc);
2838 // Stop if evaluation is true, else continue
2840 processStop(cookie.stopReason, true);
2842 postCommand("g", 0);
2847 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2849 if (exp.contains(' ') && !exp.startsWith('"')) {
2853 postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2856 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2858 postCommandSequence(command->commandSequence);
2861 // Post a sequence of standard commands: Trigger next once one completes successfully
2862 void CdbEngine::postCommandSequence(unsigned mask)
2865 qDebug("postCommandSequence 0x%x\n", mask);
2869 if (mask & CommandListThreads) {
2870 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2873 if (mask & CommandListStack) {
2874 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2877 if (mask & CommandListRegisters) {
2878 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2879 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2882 if (mask & CommandListModules) {
2883 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2886 if (mask & CommandListBreakPoints) {
2887 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2888 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2893 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2895 bool success = false;
2898 if (!reply->success) {
2899 message = QString::fromAscii(reply->errorMessage);
2902 // Should be "namespace::QWidget:0x555"
2903 QString watchExp = QString::fromAscii(reply->reply);
2904 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2906 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2909 // 0x000 -> nothing found
2910 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2911 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2914 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2915 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2916 watchExp.insert(0, QLatin1String("*("));
2917 watchHandler()->watchExpression(watchExp);
2921 showMessage(message, LogWarning);
2922 m_watchPointX = m_watchPointY = 0;
2925 static inline void formatCdbBreakPointResponse(BreakpointModelId id, const BreakpointResponse &r,
2928 str << "Obtained breakpoint " << id << " (#" << r.id.majorPart() << ')';
2932 str.setIntegerBase(16);
2933 str << ", at 0x" << r.address;
2934 str.setIntegerBase(10);
2937 str << ", disabled";
2938 if (!r.module.isEmpty())
2939 str << ", module: '" << r.module << '\'';
2943 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2945 if (debugBreakpoints)
2946 qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2947 if (!reply->success) {
2948 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2952 value.fromString(reply->reply);
2953 if (value.type() != GdbMi::List) {
2954 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2957 handleBreakPoints(value);
2960 void CdbEngine::handleBreakPoints(const GdbMi &value)
2962 // Report all obtained parameters back. Note that not all parameters are reported
2963 // back, so, match by id and complete
2964 if (debugBreakpoints)
2965 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2967 QTextStream str(&message);
2968 BreakHandler *handler = breakHandler();
2969 foreach (const GdbMi &breakPointG, value.children()) {
2970 BreakpointResponse reportedResponse;
2971 parseBreakPoint(breakPointG, &reportedResponse);
2972 if (debugBreakpoints)
2973 qDebug(" Parsed %d: pending=%d %s\n", reportedResponse.id.majorPart(),
2974 reportedResponse.pending,
2975 qPrintable(reportedResponse.toString()));
2976 if (reportedResponse.id.isValid() && !reportedResponse.pending) {
2977 const BreakpointModelId mid = handler->findBreakpointByResponseId(reportedResponse.id);
2978 QTC_ASSERT(mid.isValid(), continue; )
2979 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(mid);
2980 if (it != m_pendingBreakpointMap.end()) {
2981 // Complete the response and set on handler.
2982 BreakpointResponse ¤tResponse = it.value();
2983 currentResponse.id = reportedResponse.id;
2984 currentResponse.address = reportedResponse.address;
2985 currentResponse.module = reportedResponse.module;
2986 currentResponse.pending = reportedResponse.pending;
2987 currentResponse.enabled = reportedResponse.enabled;
2988 formatCdbBreakPointResponse(mid, currentResponse, str);
2989 if (debugBreakpoints)
2990 qDebug(" Setting for %d: %s\n", currentResponse.id.majorPart(),
2991 qPrintable(currentResponse.toString()));
2992 handler->setResponse(mid, currentResponse);
2993 m_pendingBreakpointMap.erase(it);
2995 } // not pending reported
2997 if (m_pendingBreakpointMap.empty()) {
2998 str << QLatin1String("All breakpoints have been resolved.\n");
3000 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
3002 showMessage(message, LogMisc);
3005 void CdbEngine::watchPoint(const QPoint &p)
3007 m_watchPointX = p.x();
3008 m_watchPointY = p.y();
3010 case InferiorStopOk:
3011 postWidgetAtCommand();
3014 // "Select Widget to Watch" from a running application is currently not
3015 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
3016 // but requires some work as not to confuse the engine by state-change notifications
3017 // emitted by the debuggee function call.
3018 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
3021 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
3022 arg(QString::fromAscii(stateName(state()))), LogWarning);
3027 void CdbEngine::postWidgetAtCommand()
3029 QByteArray arguments = QByteArray::number(m_watchPointX);
3030 arguments.append(' ');
3031 arguments.append(QByteArray::number(m_watchPointY));
3032 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
3035 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
3037 if (qVariantCanConvert<MemoryChangeCookie>(v)) {
3038 const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
3039 postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
3042 if (qVariantCanConvert<MemoryViewCookie>(v)) {
3043 postFetchMemory(qVariantValue<MemoryViewCookie>(v));
3048 } // namespace Internal
3049 } // namespace Debugger