1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "cdbengine.h"
35 #include "debuggerstartparameters.h"
36 #include "disassemblerlines.h"
37 #include "cdboptions.h"
38 #include "cdboptionspage.h"
39 #include "bytearrayinputstream.h"
40 #include "breakpoint.h"
41 #include "breakhandler.h"
42 #include "stackframe.h"
43 #include "stackhandler.h"
44 #include "watchhandler.h"
45 #include "threadshandler.h"
46 #include "moduleshandler.h"
47 #include "debuggeractions.h"
48 #include "debuggercore.h"
49 #include "registerhandler.h"
50 #include "disassembleragent.h"
51 #include "memoryagent.h"
52 #include "debuggerrunner.h"
53 #include "debuggertooltipmanager.h"
54 #include "cdbparsehelpers.h"
55 #include "watchutils.h"
56 #include "gdb/gdbmi.h"
57 #include "shared/cdbsymbolpathlisteditor.h"
59 #include <coreplugin/icore.h>
60 #include <texteditor/itexteditor.h>
61 #include <projectexplorer/toolchain.h>
62 #include <projectexplorer/projectexplorerconstants.h>
64 #include <utils/synchronousprocess.h>
65 #include <utils/winutils.h>
66 #include <utils/qtcassert.h>
67 #include <utils/savedaction.h>
68 #include <utils/consoleprocess.h>
70 #include <QtCore/QCoreApplication>
71 #include <QtCore/QFileInfo>
72 #include <QtCore/QDir>
73 #include <QtCore/QDebug>
74 #include <QtCore/QTextStream>
75 #include <QtCore/QDateTime>
76 #include <QtGui/QToolTip>
77 #include <QtGui/QMainWindow>
78 #include <QtGui/QMessageBox>
81 # include <utils/winutils.h>
82 # include "dbgwinutils.h"
87 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
88 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
91 enum { debugLocals = 0 };
92 enum { debugWatches = 0 };
93 enum { debugBreakpoints = 0 };
96 # define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
98 # define STATE_DEBUG(state, func, line, notifyFunc)
102 \class Debugger::Internal::CdbEngine
104 Cdb engine version 2: Run the CDB process on pipes and parse its output.
105 The engine relies on a CDB extension Qt Creator provides as an extension
106 library (32/64bit), which is loaded into cdb.exe. It serves to:
109 \o Notify the engine about the state of the debugging session:
111 \o idle: (hooked up with .idle_cmd) debuggee stopped
112 \o accessible: Debuggee stopped, cdb.exe accepts commands
113 \o inaccessible: Debuggee runs, no way to post commands
114 \o session active/inactive: Lost debuggee, terminating.
116 \o Hook up with output/event callbacks and produce formatted output to be able
117 to catch application output and exceptions.
118 \o Provide some extension commands that produce output in a standardized (GDBMI)
119 format that ends up in handleExtensionMessage(), for example:
121 \o pid Return debuggee pid for interrupting.
122 \o locals Print locals from SymbolGroup
123 \o expandLocals Expand locals in symbol group
124 \o registers, modules, threads
128 Debugger commands can be posted by calling:
132 \o postCommand(): Does not expect a reply
133 \o postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
134 that is captured by enclosing it in special tokens using the 'echo' command and
135 then invokes a callback with a CdbBuiltinCommand structure.
136 \o postExtensionCommand(): Run a command provided by the extension producing
137 one-line output and invoke a callback with a CdbExtensionCommand structure
138 (output is potentially split up in chunks).
143 [Console: The console stub launches the process. On process startup,
144 launchCDB() is called with AttachExternal].
145 setupEngine() calls launchCDB() with the startparameters. The debuggee
146 runs into the initial breakpoint (session idle). EngineSetupOk is
147 notified (inferior still stopped). setupInferior() is then called
148 which does breakpoint synchronization and issues the extension 'pid'
149 command to obtain the inferior pid (which also hooks up the output callbacks).
150 handlePid() notifies notifyInferiorSetupOk.
151 runEngine() is then called which issues 'g' to continue the inferior.
152 Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
153 when the inferior exits (except attach modes).
156 using namespace ProjectExplorer;
161 static const char localsPrefixC[] = "local.";
163 struct MemoryViewCookie
165 explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
166 quint64 addr = 0, quint64 l = 0) :
167 agent(a), editorToken(e), address(addr), length(l)
171 QObject *editorToken;
176 } // namespace Internal
177 } // namespace Debugger
179 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
184 static inline bool isConsole(const DebuggerStartParameters &sp)
186 return (sp.startMode == StartInternal || sp.startMode == StartExternal)
191 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
193 QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
194 debuggerCore()->mainWindow());
195 mb->setAttribute(Qt::WA_DeleteOnClose);
200 // Base data structure for command queue entries with callback
201 struct CdbCommandBase
203 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
206 CdbCommandBase(const QByteArray &cmd, int token, unsigned flags,
207 unsigned nc, const QVariant &cookie);
213 // Continue with another commands as specified in CommandSequenceFlags
214 unsigned commandSequence;
217 CdbCommandBase::CdbCommandBase() :
218 token(0), flags(0), commandSequence(0)
222 CdbCommandBase::CdbCommandBase(const QByteArray &cmd, int t, unsigned f,
223 unsigned nc, const QVariant &c) :
224 token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
228 // Queue entry for builtin commands producing free-format
229 // line-by-line output.
230 struct CdbBuiltinCommand : public CdbCommandBase
232 typedef CdbEngine::BuiltinCommandHandler CommandHandler;
234 CdbBuiltinCommand() {}
235 CdbBuiltinCommand(const QByteArray &cmd, int token, unsigned flags,
237 unsigned nc, const QVariant &cookie) :
238 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
242 QByteArray joinedReply() const;
244 CommandHandler handler;
245 QList<QByteArray> reply;
248 QByteArray CdbBuiltinCommand::joinedReply() const
253 answer.reserve(120 * reply.size());
254 foreach (const QByteArray &l, reply) {
261 // Queue entry for Qt Creator extension commands producing one-line
262 // output with success flag and error message.
263 struct CdbExtensionCommand : public CdbCommandBase
265 typedef CdbEngine::ExtensionCommandHandler CommandHandler;
267 CdbExtensionCommand() : success(false) {}
268 CdbExtensionCommand(const QByteArray &cmd, int token, unsigned flags,
270 unsigned nc, const QVariant &cookie) :
271 CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
273 CommandHandler handler;
275 QByteArray errorMessage;
279 template <class CommandPtrType>
280 int indexOfCommand(const QList<CommandPtrType> &l, int token)
282 const int count = l.size();
283 for (int i = 0; i < count; i++)
284 if (l.at(i)->token == token)
289 static inline bool validMode(DebuggerStartMode sm)
303 // Accessed by RunControlFactory
304 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
305 DebuggerEngine *masterEngine, QString *errorMessage)
308 CdbOptionsPage *op = CdbOptionsPage::instance();
309 if (!op || !op->options()->isValid() || !validMode(sp.startMode)) {
310 *errorMessage = QLatin1String("Internal error: Invalid start parameters passed for thre CDB engine.");
313 return new CdbEngine(sp, masterEngine, op->options());
315 Q_UNUSED(masterEngine)
318 *errorMessage = QString::fromLatin1("Unsupported debug mode");
322 bool isCdbEngineEnabled()
325 return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
331 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
333 return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
336 static QString cdbBinary(const DebuggerStartParameters &sp)
338 if (!sp.debuggerCommand.isEmpty()) {
339 // Do not use a GDB binary if we got started for a project with MinGW runtime.
340 const bool abiMatch = sp.toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS
341 && sp.toolChainAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvcFlavor;
343 return sp.debuggerCommand;
345 return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
348 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
351 if (!isCdbEngineEnabled()) {
352 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
353 arg(sp.toolChainAbi.toString()));
354 check->settingsCategory = QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
355 check->settingsPage = CdbOptionsPage::settingsId();
359 if (!validMode(sp.startMode)) {
360 check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
364 if (sp.toolChainAbi.binaryFormat() != Abi::PEFormat || sp.toolChainAbi.os() != Abi::WindowsOS) {
365 check->errorDetails.push_back(CdbEngine::tr("The CDB debug engine does not support the %1 ABI.").
366 arg(sp.toolChainAbi.toString()));
370 if (cdbBinary(sp).isEmpty()) {
371 check->errorDetails.push_back(msgNoCdbBinaryForToolChain(sp.toolChainAbi));
372 check->settingsCategory = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
373 check->settingsPage = QLatin1String(ProjectExplorer::Constants::TOOLCHAIN_SETTINGS_CATEGORY);
380 check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
385 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
388 opts->push_back(new CdbOptionsPage);
394 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
396 static inline Utils::SavedAction *theAssemblerAction()
398 return debuggerCore()->action(OperateByInstruction);
401 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
402 DebuggerEngine *masterEngine, const OptionsPtr &options) :
403 DebuggerEngine(sp, masterEngine),
404 m_creatorExtPrefix("<qtcreatorcdbext>|"),
405 m_tokenPrefix("<token>"),
407 m_effectiveStartMode(NoStartMode),
410 m_specialStopMode(NoSpecialStop),
411 m_nextCommandToken(0),
412 m_currentBuiltinCommandIndex(-1),
413 m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
414 m_operateByInstructionPending(true),
415 m_operateByInstruction(true), // Default CDB setting
416 m_notifyEngineShutdownOnTermination(false),
417 m_hasDebuggee(false),
419 m_sourceStepInto(false),
422 m_ignoreCdbOutput(false)
424 connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
426 setObjectName(QLatin1String("CdbEngine"));
427 connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
428 connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
429 connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
430 connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
433 void CdbEngine::init()
435 m_effectiveStartMode = NoStartMode;
437 m_accessible = false;
438 m_specialStopMode = NoSpecialStop;
439 m_nextCommandToken = 0;
440 m_currentBuiltinCommandIndex = -1;
441 m_operateByInstructionPending = theAssemblerAction()->isChecked();
442 m_operateByInstruction = true; // Default CDB setting
443 m_notifyEngineShutdownOnTermination = false;
444 m_hasDebuggee = false;
445 m_sourceStepInto = false;
446 m_watchPointX = m_watchPointY = 0;
447 m_ignoreCdbOutput = false;
449 m_outputBuffer.clear();
450 m_builtinCommandQueue.clear();
451 m_extensionCommandQueue.clear();
452 m_extensionMessageBuffer.clear();
453 m_pendingBreakpointMap.clear();
454 QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
457 CdbEngine::~CdbEngine()
461 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
463 if (state() == InferiorStopOk) {
464 syncOperateByInstruction(operateByInstruction);
466 // To be set next time session becomes accessible
467 m_operateByInstructionPending = operateByInstruction;
471 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
474 qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
475 if (m_operateByInstruction == operateByInstruction)
477 QTC_ASSERT(m_accessible, return; )
478 m_operateByInstruction = operateByInstruction;
479 postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
480 postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
483 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
484 TextEditor::ITextEditor *editor,
485 const DebuggerToolTipContext &contextIn)
488 qDebug() << Q_FUNC_INFO;
489 // Need a stopped debuggee and a cpp file in a valid frame
490 if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
492 // Determine expression and function
495 DebuggerToolTipContext context = contextIn;
496 const QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
497 // Are we in the current stack frame
498 if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
500 // No numerical or any other expressions [yet]
501 if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
503 const QByteArray iname = QByteArray(localsPrefixC) + exp.toAscii();
504 const QModelIndex index = watchHandler()->itemIndex(iname);
505 if (!index.isValid())
507 DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
508 tw->setContext(context);
509 tw->setDebuggerModel(LocalsWatch);
510 tw->setExpression(exp);
511 tw->acquireEngine(this);
512 DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
516 // Determine full path to the CDB extension library.
517 QString CdbEngine::extensionLibraryName(bool is64Bit)
519 // Determine extension lib name and path to use
521 QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
522 << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
523 << '/' << QT_CREATOR_CDB_EXT << ".dll";
527 // Determine environment for CDB.exe, start out with run config and
528 // add CDB extension path merged with system value should there be one.
529 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
530 QString cdbExtensionPath)
532 // Determine CDB extension path from Qt Creator
533 static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
534 const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
535 if (!oldCdbExtensionPath.isEmpty()) {
536 cdbExtensionPath.append(QLatin1Char(';'));
537 cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
539 // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
540 // config, just to make sure, delete any existing entries
541 const QString cdbExtensionPathVariableAssign =
542 QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
543 for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
544 if (it->startsWith(cdbExtensionPathVariableAssign)) {
545 it = runConfigEnvironment.erase(it);
551 runConfigEnvironment.append(cdbExtensionPathVariableAssign +
552 QDir::toNativeSeparators(cdbExtensionPath));
553 return runConfigEnvironment;
556 int CdbEngine::elapsedLogTime() const
558 const int elapsed = m_logTime.elapsed();
559 const int delta = elapsed - m_elapsedLogTime;
560 m_elapsedLogTime = elapsed;
564 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
565 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
568 qDebug("startConsole %s", qPrintable(sp.executable));
569 m_consoleStub.reset(new Utils::ConsoleProcess);
570 m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
571 connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)),
572 SLOT(consoleStubMessage(QString, bool)));
573 connect(m_consoleStub.data(), SIGNAL(processStarted()),
574 SLOT(consoleStubProcessStarted()));
575 connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
576 SLOT(consoleStubExited()));
577 m_consoleStub->setWorkingDirectory(sp.workingDirectory);
578 m_consoleStub->setEnvironment(sp.environment);
579 if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
580 *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
586 void CdbEngine::consoleStubMessage(const QString &msg, bool isError)
589 qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg));
591 if (state() == EngineSetupRequested) {
592 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
593 notifyEngineSetupFailed();
595 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
598 nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
600 showMessage(msg, AppOutput);
604 void CdbEngine::consoleStubProcessStarted()
607 qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
608 // Attach to console process.
609 DebuggerStartParameters attachParameters = startParameters();
610 attachParameters.executable.clear();
611 attachParameters.processArgs.clear();
612 attachParameters.attachPID = m_consoleStub->applicationPID();
613 attachParameters.startMode = AttachExternal;
614 showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
615 QString errorMessage;
616 if (!launchCDB(attachParameters, &errorMessage)) {
617 showMessage(errorMessage, LogError);
618 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
619 notifyEngineSetupFailed();
623 void CdbEngine::consoleStubExited()
627 void CdbEngine::setupEngine()
630 qDebug(">setupEngine");
631 // Nag to add symbol server
632 if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
633 &(m_options->symbolPaths)))
634 m_options->toSettings(Core::ICore::instance()->settings());
637 if (!m_logTime.elapsed())
639 QString errorMessage;
640 // Console: Launch the stub with the suspended application and attach to it
641 // CDB in theory has a command line option '-2' that launches a
642 // console, too, but that immediately closes when the debuggee quits.
643 // Use the Creator stub instead.
644 const DebuggerStartParameters &sp = startParameters();
645 const bool launchConsole = isConsole(sp);
646 m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
647 const bool ok = launchConsole ?
648 startConsole(startParameters(), &errorMessage) :
649 launchCDB(startParameters(), &errorMessage);
651 qDebug("<setupEngine ok=%d", ok);
653 showMessage(errorMessage, LogError);
654 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
655 notifyEngineSetupFailed();
659 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
662 qDebug("launchCDB startMode=%d", sp.startMode);
663 const QChar blank(QLatin1Char(' '));
664 // Start engine which will run until initial breakpoint:
665 // Determine binary (force MSVC), extension lib name and path to use
666 // The extension is passed as relative name with the path variable set
667 //(does not work with absolute path names)
668 const QString executable = cdbBinary(sp);
669 if (executable.isEmpty()) {
670 *errorMessage = tr("There is no CDB executable specified.");
676 Utils::winIs64BitBinary(executable);
680 const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
681 if (!extensionFi.isFile()) {
682 *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
683 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
686 const QString extensionFileName = extensionFi.fileName();
688 QStringList arguments;
689 const bool isRemote = sp.startMode == AttachToRemote;
690 if (isRemote) { // Must be first
691 arguments << QLatin1String("-remote") << sp.remoteChannel;
693 arguments << (QLatin1String("-a") + extensionFileName);
695 // Source line info/No terminal breakpoint / Pull extension
696 arguments << QLatin1String("-lines") << QLatin1String("-G")
697 // register idle (debuggee stop) notification
698 << QLatin1String("-c")
699 << QString::fromAscii(".idle_cmd " + m_extensionCommandPrefixBA + "idle");
700 if (sp.useTerminal) // Separate console
701 arguments << QLatin1String("-2");
702 if (!m_options->symbolPaths.isEmpty())
703 arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
704 if (!m_options->sourcePaths.isEmpty())
705 arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
706 // Compile argument string preserving quotes
707 QString nativeArguments = m_options->additionalArguments;
708 switch (sp.startMode) {
711 if (!nativeArguments.isEmpty())
712 nativeArguments.push_back(blank);
713 nativeArguments += QDir::toNativeSeparators(sp.executable);
718 case AttachCrashedExternal:
719 arguments << QLatin1String("-p") << QString::number(sp.attachPID);
720 if (sp.startMode == AttachCrashedExternal)
721 arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
724 *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
727 if (!sp.processArgs.isEmpty()) { // Complete native argument string.
728 if (!nativeArguments.isEmpty())
729 nativeArguments.push_back(blank);
730 nativeArguments += sp.processArgs;
733 const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
734 arg(QDir::toNativeSeparators(executable),
735 arguments.join(QString(blank)) + blank + nativeArguments,
736 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
737 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
738 showMessage(msg, LogMisc);
740 m_outputBuffer.clear();
741 m_process.setEnvironment(mergeEnvironment(sp.environment.toStringList(), extensionFi.absolutePath()));
742 if (!sp.workingDirectory.isEmpty())
743 m_process.setWorkingDirectory(sp.workingDirectory);
746 if (!nativeArguments.isEmpty()) // Appends
747 m_process.setNativeArguments(nativeArguments);
749 m_process.start(executable, arguments);
750 if (!m_process.waitForStarted()) {
751 *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
752 arg(QDir::toNativeSeparators(executable), m_process.errorString());
756 const unsigned long pid = Utils::winQPidToPid(m_process.pid());
758 const unsigned long pid = 0;
760 showMessage(QString::fromLatin1("%1 running as %2").
761 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
762 m_hasDebuggee = true;
763 if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
765 const QByteArray loadCommand = QByteArray(".load ")
766 + extensionFileName.toLocal8Bit();
767 postCommand(loadCommand, 0);
768 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
769 notifyEngineSetupOk();
774 void CdbEngine::setupInferior()
777 qDebug("setupInferior");
778 attemptBreakpointSynchronization();
779 postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
780 postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
783 void CdbEngine::runEngine()
787 // Resume the threads frozen by the console stub.
788 if (isConsole(startParameters()))
789 postCommand("~* m", 0);
790 foreach (const QString &breakEvent, m_options->breakEvents)
791 postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
795 bool CdbEngine::commandsPending() const
797 return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
800 void CdbEngine::shutdownInferior()
803 qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
804 isCdbProcessRunning());
806 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
808 qDebug("notifyInferiorShutdownOk");
809 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
810 notifyInferiorShutdownOk();
815 if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
817 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
818 notifyInferiorShutdownOk();
820 // A command got stuck.
821 if (commandsPending()) {
822 showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
823 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
824 notifyInferiorShutdownFailed();
827 if (!canInterruptInferior()) {
828 showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
829 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
830 notifyInferiorShutdownFailed();
833 interruptInferior(); // Calls us again
837 /* shutdownEngine/processFinished:
838 * Note that in the case of launching a process by the debugger, the debugger
839 * automatically quits a short time after reporting the session becoming
840 * inaccessible without debuggee (notifyInferiorExited). In that case,
841 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
842 * as not to confuse the state engine.
845 void CdbEngine::shutdownEngine()
848 qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
849 "accessible=%d,commands pending=%d",
850 stateName(state()), isCdbProcessRunning(), m_accessible,
853 if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
854 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
855 notifyEngineShutdownOk();
859 // No longer trigger anything from messages
860 m_ignoreCdbOutput = true;
861 // Go for kill if there are commands pending.
862 if (m_accessible && !commandsPending()) {
863 // detach: Wait for debugger to finish.
864 if (m_effectiveStartMode == AttachExternal)
866 // Remote requires a bit more force to quit.
867 if (m_effectiveStartMode == AttachToRemote) {
868 postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
869 postCommand("qq", 0);
873 m_notifyEngineShutdownOnTermination = true;
876 // Remote process. No can do, currently
877 m_notifyEngineShutdownOnTermination = true;
878 Utils::SynchronousProcess::stopProcess(m_process);
881 // Lost debuggee, debugger should quit anytime now
882 if (!m_hasDebuggee) {
883 m_notifyEngineShutdownOnTermination = true;
889 void CdbEngine::processFinished()
892 qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
893 elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
894 m_process.exitStatus(), m_process.exitCode());
896 const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
898 showMessage(tr("CDB crashed"), LogError); // not in your life.
900 showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
903 if (m_notifyEngineShutdownOnTermination) {
906 qDebug("notifyEngineIll");
907 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
910 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
911 notifyEngineShutdownOk();
914 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
915 notifyEngineSpontaneousShutdown();
919 void CdbEngine::detachDebugger()
921 postCommand(".detach", 0);
924 static inline bool isWatchIName(const QByteArray &iname)
926 return iname.startsWith("watch");
929 void CdbEngine::updateWatchData(const WatchData &dataIn,
930 const WatchUpdateFlags & flags)
932 if (debug || debugLocals || debugWatches)
933 qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
934 elapsedLogTime(), m_accessible, stateName(state()),
935 flags.tryIncremental,
936 qPrintable(dataIn.toString()));
938 if (!m_accessible) // Add watch data while running?
942 if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
944 ByteArrayInputStream str(args);
945 str << dataIn.iname << " \"" << dataIn.exp << '"';
946 postExtensionCommand("addwatch", args, 0,
947 &CdbEngine::handleAddWatch, 0,
948 qVariantFromValue(dataIn));
952 if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
953 WatchData data = dataIn;
954 data.setAllUnneeded();
955 watchHandler()->insertData(data);
958 updateLocalVariable(dataIn.iname);
961 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
963 WatchData item = qvariant_cast<WatchData>(reply->cookie);
965 qDebug() << "handleAddWatch ok=" << reply->success << item.iname;
966 if (reply->success) {
967 updateLocalVariable(item.iname);
969 item.setError(tr("Unable to add expression"));
970 watchHandler()->insertData(item);
971 showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
972 arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
973 reply->errorMessage), LogError);
977 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
979 if (debuggerCore()->boolSetting(VerboseLog))
980 str << blankSeparator << "-v";
981 if (debuggerCore()->boolSetting(UseDebuggingHelpers))
982 str << blankSeparator << "-c";
983 const QByteArray typeFormats = watchHandler()->typeFormatRequests();
984 if (!typeFormats.isEmpty())
985 str << blankSeparator << "-T " << typeFormats;
986 const QByteArray individualFormats = watchHandler()->individualFormatRequests();
987 if (!individualFormats.isEmpty())
988 str << blankSeparator << "-I " << individualFormats;
991 void CdbEngine::updateLocalVariable(const QByteArray &iname)
993 const bool isWatch = isWatchIName(iname);
995 qDebug() << "updateLocalVariable watch=" << isWatch << iname;
996 QByteArray localsArguments;
997 ByteArrayInputStream str(localsArguments);
998 addLocalsOptions(str);
1000 const int stackFrame = stackHandler()->currentIndex();
1001 if (stackFrame < 0) {
1002 qWarning("Internal error; no stack frame in updateLocalVariable");
1005 str << blankSeparator << stackFrame;
1007 str << blankSeparator << iname;
1008 postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1011 unsigned CdbEngine::debuggerCapabilities() const
1013 return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1014 |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
1015 |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1016 |BreakModuleCapability;
1019 void CdbEngine::executeStep()
1021 if (!m_operateByInstruction)
1022 m_sourceStepInto = true; // See explanation at handleStackTrace().
1023 postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1024 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1025 notifyInferiorRunRequested();
1028 void CdbEngine::executeStepOut()
1030 postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1031 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1032 notifyInferiorRunRequested();
1035 void CdbEngine::executeNext()
1037 postCommand(QByteArray("p"), 0); // Step over -> p
1038 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1039 notifyInferiorRunRequested();
1042 void CdbEngine::executeStepI()
1047 void CdbEngine::executeNextI()
1052 void CdbEngine::continueInferior()
1054 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1055 notifyInferiorRunRequested();
1056 doContinueInferior();
1059 void CdbEngine::doContinueInferior()
1061 postCommand(QByteArray("g"), 0);
1064 bool CdbEngine::canInterruptInferior() const
1066 return m_effectiveStartMode != AttachToRemote && m_inferiorPid;
1069 void CdbEngine::interruptInferior()
1072 qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1073 if (canInterruptInferior()) {
1074 doInterruptInferior(NoSpecialStop);
1076 showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1077 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1078 notifyInferiorStopOk();
1079 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1080 notifyInferiorRunRequested();
1081 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1082 notifyInferiorRunOk();
1086 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1089 const SpecialStopMode oldSpecialMode = m_specialStopMode;
1090 m_specialStopMode = sm;
1091 QString errorMessage;
1092 showMessage(QString::fromLatin1("Interrupting process %1...").arg(m_inferiorPid), LogMisc);
1093 if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
1094 m_specialStopMode = oldSpecialMode;
1095 showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(m_inferiorPid).arg(errorMessage), LogError);
1102 void CdbEngine::executeRunToLine(const ContextData &data)
1104 // Add one-shot breakpoint
1105 BreakpointParameters bp(BreakpointByFileAndLine);
1106 bp.fileName = data.fileName;
1107 bp.lineNumber = data.lineNumber;
1108 postCommand(cdbAddBreakpointCommand(bp, BreakpointId(-1), true), 0);
1112 void CdbEngine::executeRunToFunction(const QString &functionName)
1114 // Add one-shot breakpoint
1115 BreakpointParameters bp(BreakpointByFunction);
1116 bp.functionName = functionName;
1118 postCommand(cdbAddBreakpointCommand(bp, BreakpointId(-1), true), 0);
1122 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1124 const Registers registers = registerHandler()->registers();
1125 QTC_ASSERT(regnr < registers.size(), return)
1126 // Value is decimal or 0x-hex-prefixed
1128 ByteArrayInputStream str(cmd);
1129 str << "r " << registers.at(regnr).name << '=' << value;
1130 postCommand(cmd, 0);
1134 void CdbEngine::executeJumpToLine(const ContextData &data)
1137 ByteArrayInputStream str(cmd);
1138 // Resolve source line address and go to that location
1139 str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1140 const QVariant cookie = qVariantFromValue(data);
1141 postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1144 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1146 if (cmd->reply.isEmpty())
1148 // Evaluate expression: 5365511549 = 00000001`3fcf357d
1149 // Set register 'rip' to hex address and goto lcoation
1150 QByteArray answer = cmd->reply.front();
1151 const int equalPos = answer.indexOf(" = ");
1154 answer.remove(0, equalPos + 3);
1155 QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1156 const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1158 QByteArray registerCmd;
1159 ByteArrayInputStream str(registerCmd);
1160 // PC-register depending on 64/32bit.
1161 str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << "=0x" << answer;
1162 postCommand(registerCmd, 0);
1163 gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1166 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1169 qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1171 if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1172 qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1177 ByteArrayInputStream str(cmd);
1178 str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
1179 postCommand(cmd, 0);
1180 // Update all locals in case we change a union or something pointed to
1181 // that affects other variables, too.
1185 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1187 int currentThreadId;
1188 Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
1189 threadsHandler()->setThreads(threads);
1190 threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1191 forceCurrentThreadId : currentThreadId);
1194 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1197 qDebug("CdbEngine::handleThreads success=%d", reply->success);
1198 if (reply->success) {
1200 data.fromString(reply->reply);
1202 // Continue sequence
1203 postCommandSequence(reply->commandSequence);
1205 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1209 void CdbEngine::executeDebuggerCommand(const QString &command)
1211 postCommand(command.toLocal8Bit(), QuietCommand);
1214 // Post command without callback
1215 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1218 qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1219 elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1220 if (!(flags & QuietCommand))
1221 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1222 m_process.write(cmd + '\n');
1225 // Post a built-in-command producing free-format output with a callback.
1226 // In order to catch the output, it is enclosed in 'echo' commands
1227 // printing a specially formatted token to be identifiable in the output.
1228 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1229 BuiltinCommandHandler handler,
1230 unsigned nextCommandFlag,
1231 const QVariant &cookie)
1233 if (!m_accessible) {
1234 const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1235 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1236 showMessage(msg, LogError);
1239 if (!flags & QuietCommand)
1240 showMessage(QString::fromLocal8Bit(cmd), LogInput);
1242 const int token = m_nextCommandToken++;
1243 CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1245 m_builtinCommandQueue.push_back(pendingCommand);
1246 // Enclose command in echo-commands for token
1248 ByteArrayInputStream str(fullCmd);
1249 str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1250 << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1252 qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1253 elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1254 m_builtinCommandQueue.size(), nextCommandFlag);
1256 qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1257 fullCmd.constData());
1258 m_process.write(fullCmd);
1261 // Post an extension command producing one-line output with a callback,
1262 // pass along token for identification in queue.
1263 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1264 const QByteArray &arguments,
1266 ExtensionCommandHandler handler,
1267 unsigned nextCommandFlag,
1268 const QVariant &cookie)
1270 if (!m_accessible) {
1271 const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1272 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1273 showMessage(msg, LogError);
1277 const int token = m_nextCommandToken++;
1279 // Format full command with token to be recognizeable in the output
1281 ByteArrayInputStream str(fullCmd);
1282 str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1283 if (!arguments.isEmpty())
1284 str << ' ' << arguments;
1286 if (!flags & QuietCommand)
1287 showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1289 CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1291 m_extensionCommandQueue.push_back(pendingCommand);
1292 // Enclose command in echo-commands for token
1294 qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1295 elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1296 m_extensionCommandQueue.size(), nextCommandFlag);
1297 m_process.write(fullCmd + '\n');
1300 void CdbEngine::activateFrame(int index)
1302 // TODO: assembler,etc
1305 const StackFrames &frames = stackHandler()->frames();
1306 QTC_ASSERT(index < frames.size(), return; )
1308 const StackFrame frame = frames.at(index);
1309 if (debug || debugLocals)
1310 qDebug("activateFrame idx=%d '%s' %d", index,
1311 qPrintable(frame.file), frame.line);
1312 stackHandler()->setCurrentIndex(index);
1313 const bool showAssembler = !frames.at(index).isUsable();
1314 if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1315 watchHandler()->beginCycle();
1316 watchHandler()->endCycle();
1317 QAction *assemblerAction = theAssemblerAction();
1318 if (assemblerAction->isChecked()) {
1319 gotoLocation(frame);
1321 assemblerAction->trigger(); // Seems to trigger update
1324 gotoLocation(frame);
1329 void CdbEngine::updateLocals(bool forNewStackFrame)
1331 typedef QHash<QByteArray, int> WatcherHash;
1333 const int frameIndex = stackHandler()->currentIndex();
1334 if (frameIndex < 0) {
1335 watchHandler()->beginCycle();
1336 watchHandler()->endCycle();
1339 const StackFrame frame = stackHandler()->currentFrame();
1340 if (!frame.isUsable()) {
1341 watchHandler()->beginCycle();
1342 watchHandler()->endCycle();
1345 /* Watchers: Forcibly discard old symbol group as switching from
1346 * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1347 * and cause errors as it seems to go 'stale' when switching threads.
1348 * Initial expand, get uninitialized and query */
1349 QByteArray arguments;
1350 ByteArrayInputStream str(arguments);
1353 const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1354 if (!expanded.isEmpty()) {
1355 str << blankSeparator << "-e ";
1357 foreach(const QByteArray &e, expanded) {
1363 addLocalsOptions(str);
1364 // Uninitialized variables if desired
1365 if (debuggerCore()->boolSetting(UseCodeModel)) {
1366 QStringList uninitializedVariables;
1367 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1368 frame.function, frame.file, frame.line, &uninitializedVariables);
1369 if (!uninitializedVariables.isEmpty()) {
1370 str << blankSeparator << "-u ";
1372 foreach(const QString &u, uninitializedVariables) {
1375 str << localsPrefixC << u;
1379 // Perform watches synchronization
1380 str << blankSeparator << "-W";
1381 const WatcherHash watcherHash = WatchHandler::watcherNames();
1382 if (!watcherHash.isEmpty()) {
1383 const WatcherHash::const_iterator cend = watcherHash.constEnd();
1384 for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1385 str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1389 // Required arguments: frame
1390 str << blankSeparator << frameIndex;
1391 watchHandler()->beginCycle();
1392 postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1395 void CdbEngine::selectThread(int index)
1397 if (index < 0 || index == threadsHandler()->currentThread())
1401 const int newThreadId = threadsHandler()->threads().at(index).id;
1402 threadsHandler()->setCurrentThread(index);
1404 const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1405 postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1408 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1410 QTC_ASSERT(m_accessible, return;)
1412 ByteArrayInputStream str(cmd);
1413 str << "u " << hex << hexPrefixOn << agent->address() << " L40";
1414 const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1415 postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1418 // Parse: "00000000`77606060 cc int 3"
1419 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1421 QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1422 DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1423 DisassemblerLines disassemblerLines;
1424 foreach(const QByteArray &line, command->reply)
1425 disassemblerLines.appendLine(DisassemblerLine(QString::fromLatin1(line)));
1426 agent->setContents(disassemblerLines);
1429 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1431 if (!m_accessible) {
1432 qWarning("Internal error: Attempt to read memory from inaccessible session: %s", Q_FUNC_INFO);
1436 qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1439 ByteArrayInputStream str(args);
1440 str << addr << ' ' << length;
1441 const QVariant cookie = qVariantFromValue<MemoryViewCookie>(MemoryViewCookie(agent, editor, addr, length));
1442 postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0, cookie);
1445 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1447 QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1448 const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1449 if (command->success) {
1450 const QByteArray data = QByteArray::fromBase64(command->reply);
1451 if (unsigned(data.size()) == memViewCookie.length)
1452 memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1453 memViewCookie.address, data);
1455 showMessage(QString::fromLocal8Bit(command->errorMessage), LogError);
1459 void CdbEngine::reloadModules()
1461 postCommandSequence(CommandListModules);
1464 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1468 void CdbEngine::loadAllSymbols()
1472 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1474 Q_UNUSED(moduleName)
1477 void CdbEngine::reloadRegisters()
1479 postCommandSequence(CommandListRegisters);
1482 void CdbEngine::reloadSourceFiles()
1486 void CdbEngine::reloadFullStack()
1489 qDebug("%s", Q_FUNC_INFO);
1490 postCommandSequence(CommandListStack);
1493 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1495 if (reply->success) {
1496 m_inferiorPid = reply->reply.toUInt();
1497 showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc);
1498 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1499 notifyInferiorSetupOk();
1501 showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1502 arg(QLatin1String(reply->errorMessage)), LogError);
1503 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1504 notifyInferiorSetupFailed();
1508 // Parse CDB gdbmi register syntax
1509 static inline Register parseRegister(const GdbMi &gdbmiReg)
1512 reg.name = gdbmiReg.findChild("name").data();
1513 const GdbMi description = gdbmiReg.findChild("description");
1514 if (description.type() != GdbMi::Invalid) {
1516 reg.name += description.data();
1519 reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1523 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1525 if (reply->success) {
1527 value.fromString(reply->reply);
1528 if (value.type() == GdbMi::List) {
1530 modules.reserve(value.childCount());
1531 foreach (const GdbMi &gdbmiModule, value.children()) {
1533 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1534 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1535 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1536 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1537 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1538 module.symbolsRead = Module::ReadOk;
1539 modules.push_back(module);
1541 modulesHandler()->setModules(modules);
1543 showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1544 qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1547 showMessage(QString::fromLatin1("Failed to determine modules: %1").
1548 arg(QLatin1String(reply->errorMessage)), LogError);
1550 postCommandSequence(reply->commandSequence);
1554 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1556 if (reply->success) {
1558 value.fromString(reply->reply);
1559 if (value.type() == GdbMi::List) {
1560 Registers registers;
1561 registers.reserve(value.childCount());
1562 foreach (const GdbMi &gdbmiReg, value.children())
1563 registers.push_back(parseRegister(gdbmiReg));
1564 registerHandler()->setRegisters(registers);
1566 showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1567 qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1570 showMessage(QString::fromLatin1("Failed to determine registers: %1").
1571 arg(QLatin1String(reply->errorMessage)), LogError);
1573 postCommandSequence(reply->commandSequence);
1576 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1578 if (reply->success) {
1579 QList<WatchData> watchData;
1581 root.fromString(reply->reply);
1582 QTC_ASSERT(root.isList(), return ; )
1584 qDebug() << root.toString(true, 4);
1586 // Courtesy of GDB engine
1587 foreach (const GdbMi &child, root.children()) {
1589 dummy.iname = child.findChild("iname").data();
1590 dummy.name = QLatin1String(child.findChild("name").data());
1591 parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1593 watchHandler()->insertBulkData(watchData);
1594 watchHandler()->endCycle();
1596 QDebug nsp = qDebug().nospace();
1597 nsp << "Obtained " << watchData.size() << " items:\n";
1598 foreach (const WatchData &wd, watchData)
1599 nsp << wd.toString() <<'\n';
1601 const bool forNewStackFrame = reply->cookie.toBool();
1602 if (forNewStackFrame)
1603 emit stackFrameCompleted();
1605 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1609 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1611 if (!reply->success)
1612 showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1615 enum CdbExecutionStatus {
1616 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1617 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1618 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1619 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1620 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1621 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1622 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1624 static const char *cdbStatusName(unsigned long s)
1627 case CDB_STATUS_NO_CHANGE:
1631 case CDB_STATUS_GO_HANDLED:
1632 return "go_handled";
1633 case CDB_STATUS_GO_NOT_HANDLED:
1634 return "go_not_handled";
1635 case CDB_STATUS_STEP_OVER:
1637 case CDB_STATUS_STEP_INTO:
1639 case CDB_STATUS_BREAK:
1641 case CDB_STATUS_NO_DEBUGGEE:
1642 return "no_debuggee";
1643 case CDB_STATUS_STEP_BRANCH:
1644 return "step_branch";
1645 case CDB_STATUS_IGNORE_EVENT:
1646 return "ignore_event";
1647 case CDB_STATUS_RESTART_REQUESTED:
1648 return "restart_requested";
1649 case CDB_STATUS_REVERSE_GO:
1650 return "reverse_go";
1651 case CDB_STATUS_REVERSE_STEP_BRANCH:
1652 return "reverse_step_branch";
1653 case CDB_STATUS_REVERSE_STEP_OVER:
1654 return "reverse_step_over";
1655 case CDB_STATUS_REVERSE_STEP_INTO:
1656 return "reverse_step_into";
1661 /* Examine how to react to a stop. */
1662 enum StopActionFlags
1665 StopReportLog = 0x1,
1666 StopReportStatusMessage = 0x2,
1667 StopReportParseError = 0x4,
1668 StopShowExceptionMessageBox = 0x8,
1669 // Notify stop or just continue
1670 StopNotifyStop = 0x10,
1671 StopIgnoreContinue = 0x20,
1672 // Hit on break in artificial stop thread (created by DebugBreak()).
1673 StopInArtificialThread = 0x40,
1674 StopShutdownInProgress = 0x80 // Shutdown in progress
1677 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1679 QString *exceptionBoxMessage)
1681 // Report stop reason (GDBMI)
1683 if (targetState() == DebuggerFinished)
1684 rc |= StopShutdownInProgress;
1686 qDebug("%s", stopReason.toString(true, 4).constData());
1687 const QByteArray reason = stopReason.findChild("reason").data();
1688 if (reason.isEmpty()) {
1689 *message = tr("Malformed stop response received.");
1690 rc |= StopReportParseError|StopNotifyStop;
1693 // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1694 if (state() == InferiorStopOk) {
1695 *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1696 arg(QString::fromAscii(reason));
1697 rc |= StopReportLog;
1700 const int threadId = stopReason.findChild("threadId").data().toInt();
1701 if (reason == "breakpoint") {
1702 // Note: Internal breakpoints (run to line) are reported with id=0.
1703 BreakpointId id = 0;
1705 const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1706 if (breakpointIdG.isValid()) {
1707 id = breakpointIdG.data().toULongLong();
1709 number = breakHandler()->response(id).number;
1711 if (id && breakHandler()->type(id) == Watchpoint) {
1712 *message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
1714 *message = msgBreakpointTriggered(id, number, QString::number(threadId));
1716 rc |= StopReportStatusMessage|StopNotifyStop;
1719 if (reason == "exception") {
1720 WinException exception;
1721 exception.fromGdbMI(stopReason);
1722 QString description = exception.toString();
1724 // It is possible to hit on a startup trap while stepping (if something
1725 // pulls DLLs. Avoid showing a 'stopped' Message box.
1726 if (exception.exceptionCode == winExceptionStartupCompleteTrap)
1727 return StopNotifyStop;
1728 if (exception.exceptionCode == winExceptionCtrlPressed) {
1729 // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1730 *message = msgInterrupted();
1731 rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1734 if (isDebuggerWinException(exception.exceptionCode)) {
1735 rc |= StopReportStatusMessage|StopNotifyStop;
1736 // Detect interruption by DebugBreak() and force a switch to thread 0.
1737 if (exception.function == "ntdll!DbgBreakPoint")
1738 rc |= StopInArtificialThread;
1739 *message = msgInterrupted();
1743 *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1744 *message = description;
1745 rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1748 *message = msgStopped(QLatin1String(reason));
1749 rc |= StopReportStatusMessage|StopNotifyStop;
1753 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1759 qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1760 elapsedLogTime(), messageBA.constData(),
1761 stateName(state()), m_specialStopMode);
1763 // Switch source level debugging
1764 syncOperateByInstruction(m_operateByInstructionPending);
1766 // Engine-special stop reasons: Breakpoints and setup
1767 const SpecialStopMode specialStopMode = m_specialStopMode;
1769 m_specialStopMode = NoSpecialStop;
1771 switch(specialStopMode) {
1772 case SpecialStopSynchronizeBreakpoints:
1774 qDebug("attemptBreakpointSynchronization in special stop");
1775 attemptBreakpointSynchronization();
1776 doContinueInferior();
1778 case SpecialStopGetWidgetAt:
1779 postWidgetAtCommand();
1785 if (state() == EngineSetupRequested) { // Temporary stop at beginning
1786 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1787 notifyEngineSetupOk();
1791 // Further examine stop and report to user
1793 QString exceptionBoxMessage;
1795 stopReason.fromString(messageBA);
1796 int forcedThreadId = -1;
1797 const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage);
1798 // Do the non-blocking log reporting
1799 if (stopFlags & StopReportLog)
1800 showMessage(message, LogMisc);
1801 if (stopFlags & StopReportStatusMessage)
1802 showStatusMessage(message);
1803 if (stopFlags & StopReportParseError)
1804 showMessage(message, LogError);
1805 // Ignore things like WOW64
1806 if (stopFlags & StopIgnoreContinue) {
1807 postCommand("g", 0);
1810 // Notify about state and send off command sequence to get stack, etc.
1811 if (stopFlags & StopNotifyStop) {
1812 if (state() == InferiorStopRequested) {
1813 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1814 notifyInferiorStopOk();
1816 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
1817 notifyInferiorSpontaneousStop();
1819 // Prevent further commands from being sent if shutdown is in progress
1820 if (stopFlags & StopShutdownInProgress) {
1821 showMessage(QString::fromLatin1("Shutdown request detected..."));
1824 const bool sourceStepInto = m_sourceStepInto;
1825 m_sourceStepInto = false;
1826 // Start sequence to get all relevant data.
1827 if (stopFlags & StopInArtificialThread) {
1828 showMessage(tr("Switching to main thread..."), LogMisc);
1829 postCommand("~0 s", 0);
1831 // Re-fetch stack again.
1832 postCommandSequence(CommandListStack);
1834 const GdbMi stack = stopReason.findChild("stack");
1835 if (stack.isValid()) {
1836 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
1837 executeStep(); // Hit on a frame while step into, see parseStackTrace().
1841 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
1844 const GdbMi threads = stopReason.findChild("threads");
1845 if (threads.isValid()) {
1846 parseThreads(threads, forcedThreadId);
1848 showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
1850 // Fire off remaining commands asynchronously
1851 if (!m_pendingBreakpointMap.isEmpty())
1852 postCommandSequence(CommandListBreakPoints);
1853 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
1854 postCommandSequence(CommandListRegisters);
1855 if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
1856 postCommandSequence(CommandListModules);
1858 // After the sequence has been sent off and CDB is pondering the commands,
1859 // pop up a message box for exceptions.
1860 if (stopFlags & StopShowExceptionMessageBox)
1861 showStoppedByExceptionMessageBox(exceptionBoxMessage);
1864 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
1866 const DebuggerState s = state();
1867 if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
1871 qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
1872 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1875 case EngineShutdownRequested:
1878 case InferiorShutdownRequested:
1886 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
1888 const DebuggerState s = state();
1891 if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
1895 qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
1896 elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
1899 case EngineSetupRequested:
1901 case EngineRunRequested:
1902 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
1903 notifyEngineRunAndInferiorRunOk();
1906 case InferiorStopOk:
1907 // Inaccessible without debuggee (exit breakpoint)
1908 // We go for spontaneous engine shutdown instead.
1909 if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
1911 qDebug("Lost debuggeee");
1912 m_hasDebuggee = false;
1915 case InferiorRunRequested:
1916 STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1917 notifyInferiorRunOk();
1920 case EngineShutdownRequested:
1927 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
1930 QDebug nospace = qDebug().nospace();
1931 nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
1932 << ' ' << stateName(state());
1933 if (t == 'N' || debug > 1) {
1934 nospace << ' ' << message;
1936 nospace << ' ' << message.size() << " bytes";
1940 // Is there a reply expected, some command queued?
1941 if (t == 'R' || t == 'N') {
1942 if (token == -1) { // Default token, user typed in extension command
1943 showMessage(QString::fromLatin1(message), LogMisc);
1946 const int index = indexOfCommand(m_extensionCommandQueue, token);
1948 // Did the command finish? Take off queue and complete, invoke CB
1949 const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
1951 command->success = true;
1952 command->reply = message;
1954 command->success = false;
1955 command->errorMessage = message;
1958 qDebug("### Completed extension command '%s', token=%d, pending=%d",
1959 command->command.constData(), command->token, m_extensionCommandQueue.size());
1960 if (command->handler)
1961 (this->*(command->handler))(command);
1966 if (what == "debuggee_output") {
1967 showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
1971 if (what == "event") {
1972 showStatusMessage(QString::fromAscii(message), 5000);
1976 if (what == "session_accessible") {
1977 if (!m_accessible) {
1978 m_accessible = true;
1979 handleSessionAccessible(message.toULong());
1984 if (what == "session_inaccessible") {
1986 m_accessible = false;
1987 handleSessionInaccessible(message.toULong());
1992 if (what == "session_idle") {
1993 handleSessionIdle(message);
1997 if (what == "exception") {
1998 WinException exception;
2000 gdbmi.fromString(message);
2001 exception.fromGdbMI(gdbmi);
2002 const QString message = exception.toString(true);
2003 showStatusMessage(message);
2004 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2005 if (exception.exceptionCode == winExceptionCppException)
2006 showMessage(message + QLatin1Char('\n'), AppOutput);
2014 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2015 enum { CdbPromptLength = 7 };
2017 static inline bool isCdbPrompt(const QByteArray &c)
2019 return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2020 && std::isdigit(c.at(0)) && std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2021 && std::isdigit(c.at(4));
2024 // Check for '<token>32>' or '<token>32<'
2025 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2026 int *token, bool *isStart)
2030 const int tokenPrefixSize = tokenPrefix.size();
2031 const int size = c.size();
2032 if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2034 switch (c.at(size - 1)) {
2044 if (!c.startsWith(tokenPrefix))
2047 *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2051 void CdbEngine::parseOutputLine(QByteArray line)
2053 // The hooked output callback in the extension suppresses prompts,
2054 // it should happen only in initial and exit stages. Note however that
2055 // if the output is not hooked, sequences of prompts are possible which
2056 // can mix things up.
2057 while (isCdbPrompt(line))
2058 line.remove(0, CdbPromptLength);
2059 // An extension notification (potentially consisting of several chunks)
2060 if (line.startsWith(m_creatorExtPrefix)) {
2061 // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2062 const char type = line.at(m_creatorExtPrefix.size());
2064 const int tokenPos = m_creatorExtPrefix.size() + 2;
2065 const int tokenEndPos = line.indexOf('|', tokenPos);
2066 QTC_ASSERT(tokenEndPos != -1, return)
2067 const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2069 const int remainingChunksPos = tokenEndPos + 1;
2070 const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2071 QTC_ASSERT(remainingChunksEndPos != -1, return)
2072 const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2073 // const char 'serviceName'
2074 const int whatPos = remainingChunksEndPos + 1;
2075 const int whatEndPos = line.indexOf('|', whatPos);
2076 QTC_ASSERT(whatEndPos != -1, return)
2077 const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2078 // Build up buffer, call handler once last chunk was encountered
2079 m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2080 if (remainingChunks == 0) {
2081 handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2082 m_extensionMessageBuffer.clear();
2086 // Check for command start/end tokens within which the builtin command
2087 // output is enclosed
2089 bool isStartToken = false;
2090 const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2092 qDebug("Reading CDB stdout '%s',\n isCommand=%d, token=%d, isStart=%d, current=%d",
2093 line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2095 // If there is a current command, wait for end of output indicated by token,
2096 // command, trigger handler and finish, else append to its output.
2097 if (m_currentBuiltinCommandIndex != -1) {
2098 QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2099 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2100 if (isCommandToken) {
2101 // Did the command finish? Invoke callback and remove from queue.
2103 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2104 currentCommand->command.constData(), currentCommand->token,
2105 currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2106 QTC_ASSERT(token == currentCommand->token, return; );
2107 if (currentCommand->handler)
2108 (this->*(currentCommand->handler))(currentCommand);
2109 m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2110 m_currentBuiltinCommandIndex = -1;
2112 // Record output of current command
2113 currentCommand->reply.push_back(line);
2116 } // m_currentCommandIndex
2117 if (isCommandToken) {
2118 // Beginning command token encountered, start to record output.
2119 const int index = indexOfCommand(m_builtinCommandQueue, token);
2120 QTC_ASSERT(isStartToken && index != -1, return; );
2121 m_currentBuiltinCommandIndex = index;
2122 const CdbBuiltinCommandPtr ¤tCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2124 qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2128 showMessage(QString::fromLocal8Bit(line), LogMisc);
2131 void CdbEngine::readyReadStandardOut()
2133 if (m_ignoreCdbOutput)
2135 m_outputBuffer += m_process.readAllStandardOutput();
2136 // Split into lines and parse line by line.
2138 const int endOfLinePos = m_outputBuffer.indexOf('\n');
2139 if (endOfLinePos == -1) {
2143 QByteArray line = m_outputBuffer.left(endOfLinePos);
2144 if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2145 line.truncate(line.size() - 1);
2146 parseOutputLine(line);
2147 m_outputBuffer.remove(0, endOfLinePos + 1);
2152 void CdbEngine::readyReadStandardError()
2154 showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2157 void CdbEngine::processError()
2159 showMessage(m_process.errorString(), LogError);
2163 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2164 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2166 QByteArray cmd(cmdC);
2167 ByteArrayInputStream str(cmd);
2168 foreach(const BreakpointData *bp, bps)
2169 str << ' ' << bp->bpNumber;
2174 bool CdbEngine::stateAcceptsBreakpointChanges() const
2178 case InferiorStopOk:
2186 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2188 return DebuggerEngine::isCppBreakpoint(breakHandler()->breakpointData(id));
2191 void CdbEngine::attemptBreakpointSynchronization()
2194 qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2195 // Check if there is anything to be done at all.
2196 BreakHandler *handler = breakHandler();
2197 // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2198 foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2199 if (acceptsBreakpoint(id))
2200 handler->setEngine(id, this);
2202 // Quick check: is there a need to change something? - Populate module cache
2203 bool changed = false;
2204 const BreakpointIds ids = handler->engineBreakpointIds(this);
2205 foreach (BreakpointId id, ids) {
2206 switch (handler->state(id)) {
2207 case BreakpointInsertRequested:
2208 case BreakpointRemoveRequested:
2209 case BreakpointChangeRequested:
2212 case BreakpointInserted: {
2213 // Collect the new modules matching the files.
2214 // In the future, that information should be obtained from the build system.
2215 const BreakpointParameters &data = handler->breakpointData(id);
2216 if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2217 m_fileNameModuleHash.insert(data.fileName, data.module);
2225 if (debugBreakpoints)
2226 qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2227 elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2231 if (!m_accessible) {
2233 if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2234 doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2237 // Add/Change breakpoints and store pending ones in map, since
2238 // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2239 // handleBreakPoints will the complete that information and set it on the break handler.
2240 bool addedChanged = false;
2241 foreach (BreakpointId id, ids) {
2242 BreakpointParameters parameters = handler->breakpointData(id);
2243 BreakpointResponse response;
2244 response.fromParameters(parameters);
2245 // If we encountered that file and have a module for it: Add it.
2246 if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2247 const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2248 if (it != m_fileNameModuleHash.constEnd())
2249 parameters.module = it.value();
2251 switch (handler->state(id)) {
2252 case BreakpointInsertRequested:
2253 postCommand(cdbAddBreakpointCommand(parameters, id, false), 0);
2254 if (!parameters.enabled)
2255 postCommand("bd " + QByteArray::number(id), 0);
2256 handler->notifyBreakpointInsertProceeding(id);
2257 handler->notifyBreakpointInsertOk(id);
2258 m_pendingBreakpointMap.insert(id, response);
2259 addedChanged = true;
2260 if (debugBreakpoints)
2261 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2263 case BreakpointChangeRequested:
2264 handler->notifyBreakpointChangeProceeding(id);
2265 if (parameters.enabled != handler->response(id).enabled) {
2266 // Change enabled/disabled breakpoints without triggering update.
2267 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2268 response.pending = false;
2269 response.enabled = parameters.enabled;
2270 handler->setResponse(id, response);
2272 // Delete and re-add, triggering update
2273 addedChanged = true;
2274 postCommand("bc " + QByteArray::number(id), 0);
2275 postCommand(cdbAddBreakpointCommand(parameters, id, false), 0);
2276 m_pendingBreakpointMap.insert(id, response);
2278 handler->notifyBreakpointChangeOk(id);
2279 if (debugBreakpoints)
2280 qDebug("Changing %llu %s\n", id, qPrintable(response.toString()));
2282 case BreakpointRemoveRequested:
2283 postCommand("bc " + QByteArray::number(id), 0);
2284 handler->notifyBreakpointRemoveProceeding(id);
2285 handler->notifyBreakpointRemoveOk(id);
2286 m_pendingBreakpointMap.remove(id);
2292 // List breakpoints and send responses
2294 postCommandSequence(CommandListBreakPoints);
2297 QString CdbEngine::normalizeFileName(const QString &f)
2299 QMap<QString, QString>::const_iterator it = m_normalizedFileCache.constFind(f);
2300 if (it != m_normalizedFileCache.constEnd())
2302 const QString winF = QDir::toNativeSeparators(f);
2304 QString normalized = winNormalizeFileName(winF);
2306 QString normalized = winF;
2308 if (normalized.isEmpty()) { // At least upper case drive letter
2310 if (normalized.size() > 2 && normalized.at(1) == QLatin1Char(':'))
2311 normalized[0] = normalized.at(0).toUpper();
2313 m_normalizedFileCache.insert(f, normalized);
2317 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2318 // has more processing.
2319 static StackFrames parseFrames(const GdbMi &gdbmi)
2322 const int count = gdbmi.childCount();
2324 for (int i = 0; i < count; i++) {
2325 const GdbMi &frameMi = gdbmi.childAt(i);
2328 const GdbMi fullName = frameMi.findChild("fullname");
2329 if (fullName.isValid()) {
2330 frame.file = QFile::decodeName(fullName.data());
2331 frame.line = frameMi.findChild("line").data().toInt();
2332 frame.usable = QFileInfo(frame.file).isFile();
2334 frame.function = QLatin1String(frameMi.findChild("func").data());
2335 frame.from = QLatin1String(frameMi.findChild("from").data());
2336 frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2337 rc.push_back(frame);
2342 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2344 // Parse frames, find current. Special handling for step into:
2345 // When stepping into on an actual function (source mode) by executing 't', an assembler
2346 // frame pointing at the jmp instruction is hit (noticeable by top function being
2347 // 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
2348 // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2350 StackFrames frames = parseFrames(data);
2351 const int count = frames.size();
2352 for (int i = 0; i < count; i++) {
2353 const bool hasFile = !frames.at(i).file.isEmpty();
2354 // jmp-frame hit by step into, do another 't' and abort sequence.
2355 if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2356 showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2357 return ParseStackStepInto;
2360 frames[i].file = QDir::cleanPath(normalizeFileName(frames.at(i).file));
2361 if (current == -1 && frames[i].usable)
2365 if (count && current == -1) // No usable frame, use assembly.
2368 stackHandler()->setFrames(frames);
2369 activateFrame(current);
2373 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2375 if (command->success) {
2377 data.fromString(command->reply);
2378 parseStackTrace(data, false);
2379 postCommandSequence(command->commandSequence);
2381 showMessage(command->errorMessage, LogError);
2385 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2387 postCommandSequence(command->commandSequence);
2390 // Post a sequence of standard commands: Trigger next once one completes successfully
2391 void CdbEngine::postCommandSequence(unsigned mask)
2394 qDebug("postCommandSequence 0x%x\n", mask);
2398 if (mask & CommandListThreads) {
2399 postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2402 if (mask & CommandListStack) {
2403 postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2406 if (mask & CommandListRegisters) {
2407 QTC_ASSERT(threadsHandler()->currentThread() >= 0, return; )
2408 postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2411 if (mask & CommandListModules) {
2412 postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2415 if (mask & CommandListBreakPoints) {
2416 postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2417 &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2422 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2424 bool success = false;
2427 if (!reply->success) {
2428 message = QString::fromAscii(reply->errorMessage);
2431 // Should be "namespace::QWidget:0x555"
2432 QString watchExp = QString::fromAscii(reply->reply);
2433 const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2435 message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2438 // 0x000 -> nothing found
2439 if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2440 message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2443 // Turn into watch expression: "*(namespace::QWidget*)0x555"
2444 watchExp.replace(sepPos, 1, QLatin1String("*)"));
2445 watchExp.insert(0, QLatin1String("*("));
2446 watchHandler()->watchExpression(watchExp);
2450 showMessage(message, LogWarning);
2451 m_watchPointX = m_watchPointY = 0;
2454 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2457 str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2461 str.setIntegerBase(16);
2462 str << ", at 0x" << r.address;
2463 str.setIntegerBase(10);
2466 str << ", disabled";
2467 if (!r.module.isEmpty())
2468 str << ", module: '" << r.module << '\'';
2472 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2474 if (debugBreakpoints)
2475 qDebug("CdbEngine::handleBreakPoints: sucess=%d: %s", reply->success, reply->reply.constData());
2476 if (!reply->success) {
2477 showMessage(QString::fromAscii(reply->errorMessage), LogError);
2481 value.fromString(reply->reply);
2482 if (value.type() != GdbMi::List) {
2483 showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2486 handleBreakPoints(value);
2489 void CdbEngine::handleBreakPoints(const GdbMi &value)
2491 // Report all obtained parameters back. Note that not all parameters are reported
2492 // back, so, match by id and complete
2493 if (debugBreakpoints)
2494 qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2496 QTextStream str(&message);
2497 BreakHandler *handler = breakHandler();
2498 foreach (const GdbMi &breakPointG, value.children()) {
2499 BreakpointResponse reportedResponse;
2500 const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2501 if (debugBreakpoints)
2502 qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2503 qPrintable(reportedResponse.toString()));
2505 if (!reportedResponse.pending) {
2506 const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2507 if (it != m_pendingBreakpointMap.end()) {
2508 // Complete the response and set on handler.
2509 BreakpointResponse ¤tResponse = it.value();
2510 currentResponse.number = reportedResponse.number;
2511 currentResponse.address = reportedResponse.address;
2512 currentResponse.module = reportedResponse.module;
2513 currentResponse.pending = reportedResponse.pending;
2514 currentResponse.enabled = reportedResponse.enabled;
2515 formatCdbBreakPointResponse(id, currentResponse, str);
2516 handler->setResponse(id, currentResponse);
2517 m_pendingBreakpointMap.erase(it);
2519 } // not pending reported
2521 if (m_pendingBreakpointMap.empty()) {
2522 str << QLatin1String("All breakpoints have been resolved.\n");
2524 str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2526 showMessage(message, LogMisc);
2529 void CdbEngine::watchPoint(const QPoint &p)
2531 m_watchPointX = p.x();
2532 m_watchPointY = p.y();
2534 case InferiorStopOk:
2535 postWidgetAtCommand();
2538 // "Select Widget to Watch" from a running application is currently not
2539 // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2540 // but requires some work as not to confuse the engine by state-change notifications
2541 // emitted by the debuggee function call.
2542 showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2545 showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2546 arg(QString::fromAscii(stateName(state()))), LogWarning);
2551 void CdbEngine::postWidgetAtCommand()
2553 QByteArray arguments = QByteArray::number(m_watchPointX);
2554 arguments.append(' ');
2555 arguments.append(QByteArray::number(m_watchPointY));
2556 postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2559 } // namespace Internal
2560 } // namespace Debugger