OSDN Git Service

Merge remote-tracking branch 'origin/2.2'
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / cdb / cdbengine.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
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"
58
59 #include <TranslationUnit.h>
60
61 #include <coreplugin/icore.h>
62 #include <texteditor/itexteditor.h>
63 #include <projectexplorer/abi.h>
64 #include <projectexplorer/projectexplorerconstants.h>
65
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>
72
73 #include <cplusplus/findcdbbreakpoint.h>
74 #include <cplusplus/CppDocument.h>
75 #include <cplusplus/ModelManagerInterface.h>
76
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>
86
87 #ifdef Q_OS_WIN
88 #    include <utils/winutils.h>
89 #    include "dbgwinutils.h"
90 #endif
91
92 #include <cctype>
93
94 Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
95 Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
96
97 enum { debug = 0 };
98 enum { debugLocals = 0 };
99 enum { debugSourceMapping = 0 };
100 enum { debugWatches = 0 };
101 enum { debugBreakpoints = 0 };
102
103 #if 0
104 #  define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
105 #else
106 #  define STATE_DEBUG(state, func, line, notifyFunc)
107 #endif
108
109 /*!
110     \class Debugger::Internal::CdbEngine
111
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:
115
116     \list
117     \o Notify the engine about the state of the debugging session:
118         \list
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.
123         \endlist
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:
128       \list
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
133       \endlist
134    \endlist
135
136    Debugger commands can be posted by calling:
137
138    \list
139
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).
147     \endlist
148
149
150     Startup sequence:
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).
162 */
163
164 using namespace ProjectExplorer;
165
166 namespace Debugger {
167 namespace Internal {
168
169 static const char localsPrefixC[] = "local.";
170
171 struct MemoryViewCookie
172 {
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)
176     {}
177
178     MemoryAgent *agent;
179     QObject *editorToken;
180     quint64 address;
181     quint64 length;
182 };
183
184 struct MemoryChangeCookie
185 {
186     explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
187                                address(addr), data(d) {}
188
189     quint64 address;
190     QByteArray data;
191 };
192
193 struct ConditionalBreakPointCookie
194 {
195     ConditionalBreakPointCookie(BreakpointId i = 0) : id(i) {}
196     BreakpointId id;
197     GdbMi stopReason;
198 };
199
200 } // namespace Internal
201 } // namespace Debugger
202
203 Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
204 Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
205 Q_DECLARE_METATYPE(Debugger::Internal::ConditionalBreakPointCookie)
206
207 namespace Debugger {
208 namespace Internal {
209
210 static inline bool isCreatorConsole(const DebuggerStartParameters &sp, const CdbOptions &o)
211 {
212     return !o.cdbConsole && sp.useTerminal
213            && (sp.startMode == StartInternal || sp.startMode == StartExternal);
214 }
215
216 static QMessageBox *
217 nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text)
218 {
219     QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok,
220                                       debuggerCore()->mainWindow());
221     mb->setAttribute(Qt::WA_DeleteOnClose);
222     mb->show();
223     return mb;
224 }
225
226 // Base data structure for command queue entries with callback
227 struct CdbCommandBase
228 {
229     typedef CdbEngine::BuiltinCommandHandler CommandHandler;
230
231     CdbCommandBase();
232     CdbCommandBase(const QByteArray  &cmd, int token, unsigned flags,
233                    unsigned nc, const QVariant &cookie);
234
235     int token;
236     unsigned flags;
237     QByteArray command;
238     QVariant cookie;
239     // Continue with another commands as specified in CommandSequenceFlags
240     unsigned commandSequence;
241 };
242
243 CdbCommandBase::CdbCommandBase() :
244     token(0), flags(0), commandSequence(0)
245 {
246 }
247
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)
251 {
252 }
253
254 // Queue entry for builtin commands producing free-format
255 // line-by-line output.
256 struct CdbBuiltinCommand : public CdbCommandBase
257 {
258     typedef CdbEngine::BuiltinCommandHandler CommandHandler;
259
260     CdbBuiltinCommand() {}
261     CdbBuiltinCommand(const QByteArray  &cmd, int token, unsigned flags,
262                       CommandHandler h,
263                       unsigned nc, const QVariant &cookie) :
264         CdbCommandBase(cmd, token, flags, nc, cookie), handler(h)
265     {}
266
267
268     QByteArray joinedReply() const;
269
270     CommandHandler handler;
271     QList<QByteArray> reply;
272 };
273
274 QByteArray CdbBuiltinCommand::joinedReply() const
275 {
276     if (reply.isEmpty())
277         return QByteArray();
278     QByteArray answer;
279     answer.reserve(120  * reply.size());
280     foreach (const QByteArray &l, reply) {
281         answer += l;
282         answer += '\n';
283     }
284     return answer;
285 }
286
287 // Queue entry for Qt Creator extension commands producing one-line
288 // output with success flag and error message.
289 struct CdbExtensionCommand : public CdbCommandBase
290 {
291     typedef CdbEngine::ExtensionCommandHandler CommandHandler;
292
293     CdbExtensionCommand() : success(false) {}
294     CdbExtensionCommand(const QByteArray  &cmd, int token, unsigned flags,
295                       CommandHandler h,
296                       unsigned nc, const QVariant &cookie) :
297         CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}
298
299     CommandHandler handler;
300     QByteArray reply;
301     QByteArray errorMessage;
302     bool success;
303 };
304
305 template <class CommandPtrType>
306 int indexOfCommand(const QList<CommandPtrType> &l, int token)
307 {
308     const int count = l.size();
309     for (int i = 0; i < count; i++)
310         if (l.at(i)->token == token)
311             return i;
312     return -1;
313 }
314
315 static inline bool validMode(DebuggerStartMode sm)
316 {
317     switch (sm) {
318     case NoStartMode:
319     case AttachCore:
320     case StartRemoteGdb:
321         return false;
322     default:
323         break;
324     }
325     return true;
326 }
327
328 // Accessed by RunControlFactory
329 DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
330     DebuggerEngine *masterEngine, QString *errorMessage)
331 {
332 #ifdef Q_OS_WIN
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.");
336         return 0;
337     }
338     return new CdbEngine(sp, masterEngine, op->options());
339 #else
340     Q_UNUSED(masterEngine)
341     Q_UNUSED(sp)
342 #endif
343     *errorMessage = QString::fromLatin1("Unsupported debug mode");
344     return 0;
345 }
346
347 bool isCdbEngineEnabled()
348 {
349 #ifdef Q_OS_WIN
350     return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
351 #else
352     return false;
353 #endif
354 }
355
356 static inline QString msgNoCdbBinaryForToolChain(const ProjectExplorer::Abi &tc)
357 {
358     return CdbEngine::tr("There is no CDB binary available for binaries in format '%1'").arg(tc.toString());
359 }
360
361 static QString cdbBinary(const DebuggerStartParameters &sp)
362 {
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);
369         if (abiMatch)
370             return sp.debuggerCommand;
371     }
372     return debuggerCore()->debuggerForAbi(sp.toolChainAbi, CdbEngineType);
373 }
374
375 bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check)
376 {
377 #ifdef Q_OS_WIN
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();
383         return false;
384     }
385
386     if (!validMode(sp.startMode)) {
387         check->errorDetails.push_back(CdbEngine::tr("The CDB engine does not support start mode %1.").arg(sp.startMode));
388         return false;
389     }
390
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()));
394         return false;
395     }
396
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);
401         return false;
402     }
403
404     return true;
405 #else
406     Q_UNUSED(sp);
407     check->errorDetails.push_back(QString::fromLatin1("Unsupported debug mode"));
408     return false;
409 #endif
410 }
411
412 void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
413 {
414 #ifdef Q_OS_WIN
415     opts->push_back(new CdbOptionsPage);
416 #else
417     Q_UNUSED(opts);
418 #endif
419 }
420
421 #define QT_CREATOR_CDB_EXT "qtcreatorcdbext"
422
423 static inline Utils::SavedAction *theAssemblerAction()
424 {
425     return debuggerCore()->action(OperateByInstruction);
426 }
427
428 CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
429         DebuggerEngine *masterEngine, const OptionsPtr &options) :
430     DebuggerEngine(sp, masterEngine),
431     m_creatorExtPrefix("<qtcreatorcdbext>|"),
432     m_tokenPrefix("<token>"),
433     m_options(options),
434     m_effectiveStartMode(NoStartMode),
435     m_accessible(false),
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),
444     m_elapsedLogTime(0),
445     m_sourceStepInto(false),
446     m_watchPointX(0),
447     m_watchPointY(0),
448     m_ignoreCdbOutput(false)
449 {
450     connect(theAssemblerAction(), SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));
451
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()));
457 }
458
459 void CdbEngine::init()
460 {
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;
474
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
482     // Create local list of mappings in native separators
483     m_sourcePathMappings.clear();
484     const QSharedPointer<GlobalDebuggerOptions> globalOptions = debuggerCore()->globalDebuggerOptions();
485     if (!globalOptions->sourcePathMap.isEmpty()) {
486         typedef GlobalDebuggerOptions::SourcePathMap::const_iterator SourcePathMapIterator;
487         m_sourcePathMappings.reserve(globalOptions->sourcePathMap.size());
488         const SourcePathMapIterator cend = globalOptions->sourcePathMap.constEnd();
489         for (SourcePathMapIterator it = globalOptions->sourcePathMap.constBegin(); it != cend; ++it) {
490             m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
491                                                              QDir::toNativeSeparators(it.value())));
492         }
493     }
494     QTC_ASSERT(m_process.state() != QProcess::Running, Utils::SynchronousProcess::stopProcess(m_process); )
495 }
496
497 CdbEngine::~CdbEngine()
498 {
499 }
500
501 void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
502 {
503     // To be set next time session becomes accessible
504     m_operateByInstructionPending = operateByInstruction;
505     if (state() == InferiorStopOk)
506         syncOperateByInstruction(operateByInstruction);
507 }
508
509 void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
510 {
511     if (debug)
512         qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
513     if (m_operateByInstruction == operateByInstruction)
514         return;
515     QTC_ASSERT(m_accessible, return; )
516     m_operateByInstruction = operateByInstruction;
517     postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
518     postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
519 }
520
521 bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
522                                      TextEditor::ITextEditor *editor,
523                                      const DebuggerToolTipContext &contextIn)
524 {
525     if (debug)
526         qDebug() << Q_FUNC_INFO;
527     // Need a stopped debuggee and a cpp file in a valid frame
528     if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
529         return false;
530     // Determine expression and function
531     int line;
532     int column;
533     DebuggerToolTipContext context = contextIn;
534     QString exp = cppExpressionAt(editor, context.position, &line, &column, &context.function);
535     // Are we in the current stack frame
536     if (context.function.isEmpty() || exp.isEmpty() || context.function != stackHandler()->currentFrame().function)
537         return false;
538     // No numerical or any other expressions [yet]
539     if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
540         return false;
541     // Can this be found as a local variable?
542     const QByteArray localsPrefix(localsPrefixC);
543     QByteArray iname = localsPrefix + exp.toAscii();
544     QModelIndex index = watchHandler()->itemIndex(iname);
545     if (!index.isValid()) {
546         // Nope, try a 'local.this.m_foo'.
547         exp.prepend(QLatin1String("this."));
548         iname.insert(localsPrefix.size(), "this.");
549         index = watchHandler()->itemIndex(iname);
550         if (!index.isValid())
551             return false;
552     }
553     DebuggerTreeViewToolTipWidget *tw = new DebuggerTreeViewToolTipWidget;
554     tw->setContext(context);
555     tw->setDebuggerModel(LocalsWatch);
556     tw->setExpression(exp);
557     tw->acquireEngine(this);
558     DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
559     return true;
560 }
561
562 // Determine full path to the CDB extension library.
563 QString CdbEngine::extensionLibraryName(bool is64Bit)
564 {
565     // Determine extension lib name and path to use
566     QString rc;
567     QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
568                      << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
569                      << '/' << QT_CREATOR_CDB_EXT << ".dll";
570     return rc;
571 }
572
573 // Determine environment for CDB.exe, start out with run config and
574 // add CDB extension path merged with system value should there be one.
575 static QStringList mergeEnvironment(QStringList runConfigEnvironment,
576                                     QString cdbExtensionPath)
577 {
578     // Determine CDB extension path from Qt Creator
579     static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
580     const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
581     if (!oldCdbExtensionPath.isEmpty()) {
582         cdbExtensionPath.append(QLatin1Char(';'));
583         cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
584     }
585     // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
586     // config, just to make sure, delete any existing entries
587     const QString cdbExtensionPathVariableAssign =
588             QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
589     for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
590         if (it->startsWith(cdbExtensionPathVariableAssign)) {
591             it = runConfigEnvironment.erase(it);
592             break;
593         } else {
594             ++it;
595         }
596     }
597     runConfigEnvironment.append(cdbExtensionPathVariableAssign +
598                                 QDir::toNativeSeparators(cdbExtensionPath));
599     return runConfigEnvironment;
600 }
601
602 int CdbEngine::elapsedLogTime() const
603 {
604     const int elapsed = m_logTime.elapsed();
605     const int delta = elapsed - m_elapsedLogTime;
606     m_elapsedLogTime = elapsed;
607     return delta;
608 }
609
610 // Start the console stub with the sub process. Continue in consoleStubProcessStarted.
611 bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage)
612 {
613     if (debug)
614         qDebug("startConsole %s", qPrintable(sp.executable));
615     m_consoleStub.reset(new Utils::ConsoleProcess);
616     m_consoleStub->setMode(Utils::ConsoleProcess::Suspend);
617     connect(m_consoleStub.data(), SIGNAL(processError(QString)),
618             SLOT(consoleStubError(QString)));
619     connect(m_consoleStub.data(), SIGNAL(processStarted()),
620             SLOT(consoleStubProcessStarted()));
621     connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
622             SLOT(consoleStubExited()));
623     m_consoleStub->setWorkingDirectory(sp.workingDirectory);
624     if (sp.environment.size())
625         m_consoleStub->setEnvironment(sp.environment);
626     if (!m_consoleStub->start(sp.executable, sp.processArgs)) {
627         *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable);
628         return false;
629     }
630     return true;
631 }
632
633 void CdbEngine::consoleStubError(const QString &msg)
634 {
635     if (debug)
636         qDebug("consoleStubProcessMessage() in %s %s", stateName(state()), qPrintable(msg));
637     if (state() == EngineSetupRequested) {
638         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
639         notifyEngineSetupFailed();
640     } else {
641         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
642         notifyEngineIll();
643     }
644     nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg);
645 }
646
647 void CdbEngine::consoleStubProcessStarted()
648 {
649     if (debug)
650         qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
651     // Attach to console process.
652     DebuggerStartParameters attachParameters = startParameters();
653     attachParameters.executable.clear();
654     attachParameters.processArgs.clear();
655     attachParameters.attachPID = m_consoleStub->applicationPID();
656     attachParameters.startMode = AttachExternal;
657     attachParameters.useTerminal = false;
658     showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
659     QString errorMessage;
660     if (!launchCDB(attachParameters, &errorMessage)) {
661         showMessage(errorMessage, LogError);
662         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
663         notifyEngineSetupFailed();
664     }
665 }
666
667 void CdbEngine::consoleStubExited()
668 {
669 }
670
671 void CdbEngine::setupEngine()
672 {
673     if (debug)
674         qDebug(">setupEngine");
675     // Nag to add symbol server
676     if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
677                                                          &(m_options->symbolPaths)))
678         m_options->toSettings(Core::ICore::instance()->settings());
679
680     init();
681     if (!m_logTime.elapsed())
682         m_logTime.start();
683     QString errorMessage;
684     // Console: Launch the stub with the suspended application and attach to it
685     // CDB in theory has a command line option '-2' that launches a
686     // console, too, but that immediately closes when the debuggee quits.
687     // Use the Creator stub instead.
688     const DebuggerStartParameters &sp = startParameters();
689     const bool launchConsole = isCreatorConsole(sp, *m_options);
690     m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode;
691     const bool ok = launchConsole ?
692                 startConsole(startParameters(), &errorMessage) :
693                 launchCDB(startParameters(), &errorMessage);
694     if (debug)
695         qDebug("<setupEngine ok=%d", ok);
696     if (!ok) {
697         showMessage(errorMessage, LogError);
698         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
699         notifyEngineSetupFailed();
700     }
701 }
702
703 bool CdbEngine::launchCDB(const DebuggerStartParameters &sp, QString *errorMessage)
704 {
705     if (debug)
706         qDebug("launchCDB startMode=%d", sp.startMode);
707     const QChar blank(QLatin1Char(' '));
708     // Start engine which will run until initial breakpoint:
709     // Determine binary (force MSVC), extension lib name and path to use
710     // The extension is passed as relative name with the path variable set
711     //(does not work with absolute path names)
712     const QString executable = cdbBinary(sp);
713     if (executable.isEmpty()) {
714         *errorMessage = tr("There is no CDB executable specified.");
715         return false;
716     }
717
718     const bool is64bit =
719 #ifdef Q_OS_WIN
720             Utils::winIs64BitBinary(executable);
721 #else
722             false;
723 #endif
724     const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64bit));
725     if (!extensionFi.isFile()) {
726         *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
727                 arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
728         return false;
729     }
730     const QString extensionFileName = extensionFi.fileName();
731     // Prepare arguments
732     QStringList arguments;
733     const bool isRemote = sp.startMode == AttachToRemote;
734     if (isRemote) { // Must be first
735         arguments << QLatin1String("-remote") << sp.remoteChannel;
736     } else {
737         arguments << (QLatin1String("-a") + extensionFileName);
738     }
739     // Source line info/No terminal breakpoint / Pull extension
740     arguments << QLatin1String("-lines") << QLatin1String("-G")
741     // register idle (debuggee stop) notification
742               << QLatin1String("-c")
743               << QLatin1String(".idle_cmd ") + QString::fromAscii(m_extensionCommandPrefixBA) + QLatin1String("idle");
744     if (sp.useTerminal) // Separate console
745         arguments << QLatin1String("-2");
746     if (!m_options->symbolPaths.isEmpty())
747         arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
748     if (!m_options->sourcePaths.isEmpty())
749         arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
750     // Compile argument string preserving quotes
751     QString nativeArguments = m_options->additionalArguments;
752     switch (sp.startMode) {
753     case StartInternal:
754     case StartExternal:
755         if (!nativeArguments.isEmpty())
756             nativeArguments.push_back(blank);
757         nativeArguments += QDir::toNativeSeparators(sp.executable);
758         break;
759     case AttachToRemote:
760         break;
761     case AttachExternal:
762     case AttachCrashedExternal:
763         arguments << QLatin1String("-p") << QString::number(sp.attachPID);
764         if (sp.startMode == AttachCrashedExternal) {
765             arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
766         } else {
767             if (isCreatorConsole(startParameters(), *m_options))
768                 arguments << QLatin1String("-pr") << QLatin1String("-pb");
769         }
770         break;
771     default:
772         *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
773         return false;
774     }
775     if (!sp.processArgs.isEmpty()) { // Complete native argument string.
776         if (!nativeArguments.isEmpty())
777             nativeArguments.push_back(blank);
778         nativeArguments += sp.processArgs;
779     }
780
781     const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
782             arg(QDir::toNativeSeparators(executable),
783                 arguments.join(QString(blank)) + blank + nativeArguments,
784                 QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
785                 extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
786     showMessage(msg, LogMisc);
787
788     m_outputBuffer.clear();
789     const QStringList environment = sp.environment.size() == 0 ?
790                                     QProcessEnvironment::systemEnvironment().toStringList() :
791                                     sp.environment.toStringList();
792     m_process.setEnvironment(mergeEnvironment(environment, extensionFi.absolutePath()));
793     if (!sp.workingDirectory.isEmpty())
794         m_process.setWorkingDirectory(sp.workingDirectory);
795
796 #ifdef Q_OS_WIN
797     if (!nativeArguments.isEmpty()) // Appends
798         m_process.setNativeArguments(nativeArguments);
799 #endif
800     m_process.start(executable, arguments);
801     if (!m_process.waitForStarted()) {
802         *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
803                 arg(QDir::toNativeSeparators(executable), m_process.errorString());
804         return false;
805     }
806 #ifdef Q_OS_WIN
807     const unsigned long pid = Utils::winQPidToPid(m_process.pid());
808 #else
809     const unsigned long pid = 0;
810 #endif
811     showMessage(QString::fromLatin1("%1 running as %2").
812                 arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
813     m_hasDebuggee = true;
814     if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
815         m_accessible = true;
816         const QByteArray loadCommand = QByteArray(".load ")
817                 + extensionFileName.toLocal8Bit();
818         postCommand(loadCommand, 0);
819         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
820         notifyEngineSetupOk();
821     }
822     return true;
823 }
824
825 void CdbEngine::setupInferior()
826 {
827     if (debug)
828         qDebug("setupInferior");
829     attemptBreakpointSynchronization();
830
831     if (startParameters().breakOnMain) {
832         const BreakpointParameters bp(BreakpointAtMain);
833         postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings,
834                                             BreakpointId(-1), true), 0);
835     }
836     postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
837     postCommand(".asm source_line", 0); // Source line in assembly
838     postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
839 }
840
841 void CdbEngine::runEngine()
842 {
843     if (debug)
844         qDebug("runEngine");
845     foreach (const QString &breakEvent, m_options->breakEvents)
846             postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
847     postCommand("g", 0);
848 }
849
850 bool CdbEngine::commandsPending() const
851 {
852     return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
853 }
854
855 void CdbEngine::shutdownInferior()
856 {
857     if (debug)
858         qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
859                isCdbProcessRunning());
860
861     if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
862         if (debug)
863             qDebug("notifyInferiorShutdownOk");
864         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
865         notifyInferiorShutdownOk();
866         return;
867     }
868
869     if (m_accessible) {
870         if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal)
871             detachDebugger();
872         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
873         notifyInferiorShutdownOk();
874     } else {
875         // A command got stuck.
876         if (commandsPending()) {
877             showMessage(QLatin1String("Cannot shut down inferior due to pending commands."), LogWarning);
878             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
879             notifyInferiorShutdownFailed();
880             return;
881         }
882         if (!canInterruptInferior()) {
883             showMessage(QLatin1String("Cannot interrupt the inferior."), LogWarning);
884             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
885             notifyInferiorShutdownFailed();
886             return;
887         }
888         interruptInferior(); // Calls us again
889     }
890 }
891
892 /* shutdownEngine/processFinished:
893  * Note that in the case of launching a process by the debugger, the debugger
894  * automatically quits a short time after reporting the session becoming
895  * inaccessible without debuggee (notifyInferiorExited). In that case,
896  * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
897  * as not to confuse the state engine.
898  */
899
900 void CdbEngine::shutdownEngine()
901 {
902     if (debug)
903         qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
904                "accessible=%d,commands pending=%d",
905                stateName(state()), isCdbProcessRunning(), m_accessible,
906                commandsPending());
907
908     if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
909         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
910         notifyEngineShutdownOk();
911         return;
912     }
913
914     // No longer trigger anything from messages
915     m_ignoreCdbOutput = true;
916     // Go for kill if there are commands pending.
917     if (m_accessible && !commandsPending()) {
918         // detach: Wait for debugger to finish.
919         if (m_effectiveStartMode == AttachExternal)
920             detachDebugger();
921         // Remote requires a bit more force to quit.
922         if (m_effectiveStartMode == AttachToRemote) {
923             postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
924             postCommand("qq", 0);
925         } else {
926             postCommand("q", 0);
927         }
928         m_notifyEngineShutdownOnTermination = true;
929         return;
930     } else {
931         // Remote process. No can do, currently
932         m_notifyEngineShutdownOnTermination = true;
933         Utils::SynchronousProcess::stopProcess(m_process);
934         return;
935     }
936     // Lost debuggee, debugger should quit anytime now
937     if (!m_hasDebuggee) {
938         m_notifyEngineShutdownOnTermination = true;
939         return;
940     }
941     interruptInferior();
942 }
943
944 void CdbEngine::processFinished()
945 {
946     if (debug)
947         qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
948                elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
949                m_process.exitStatus(), m_process.exitCode());
950
951     const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
952     if (crashed) {
953         showMessage(tr("CDB crashed"), LogError); // not in your life.
954     } else {
955         showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
956     }
957
958     if (m_notifyEngineShutdownOnTermination) {
959         if (crashed) {
960             if (debug)
961                 qDebug("notifyEngineIll");
962             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
963             notifyEngineIll();
964         } else {
965             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
966             notifyEngineShutdownOk();
967         }
968     } else {
969         // The QML/CPP engine relies on the standard sequence of InferiorShutDown,etc.
970         // Otherwise, we take a shortcut.
971         if (isSlaveEngine()) {
972             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorExited")
973             notifyInferiorExited();
974         } else {
975             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown")
976             notifyEngineSpontaneousShutdown();
977         }
978     }
979 }
980
981 void CdbEngine::detachDebugger()
982 {
983     postCommand(".detach", 0);
984 }
985
986 static inline bool isWatchIName(const QByteArray &iname)
987 {
988     return iname.startsWith("watch");
989 }
990
991 void CdbEngine::updateWatchData(const WatchData &dataIn,
992                                 const WatchUpdateFlags & flags)
993 {
994     if (debug || debugLocals || debugWatches)
995         qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
996                elapsedLogTime(), m_accessible, stateName(state()),
997                flags.tryIncremental,
998                qPrintable(dataIn.toString()));
999
1000     if (!m_accessible) // Add watch data while running?
1001         return;
1002
1003     // New watch item?
1004     if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) {
1005         QByteArray args;
1006         ByteArrayInputStream str(args);
1007         str << dataIn.iname << " \"" << dataIn.exp << '"';
1008         postExtensionCommand("addwatch", args, 0,
1009                              &CdbEngine::handleAddWatch, 0,
1010                              qVariantFromValue(dataIn));
1011         return;
1012     }
1013
1014     if (!dataIn.hasChildren && !dataIn.isValueNeeded()) {
1015         WatchData data = dataIn;
1016         data.setAllUnneeded();
1017         watchHandler()->insertData(data);
1018         return;
1019     }
1020     updateLocalVariable(dataIn.iname);
1021 }
1022
1023 void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
1024 {
1025     WatchData item = qvariant_cast<WatchData>(reply->cookie);
1026     if (debugWatches)
1027         qDebug() << "handleAddWatch ok="  << reply->success << item.iname;
1028     if (reply->success) {
1029         updateLocalVariable(item.iname);
1030     } else {
1031         item.setError(tr("Unable to add expression"));
1032         watchHandler()->insertData(item);
1033         showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3").
1034                     arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp),
1035                         reply->errorMessage), LogError);
1036     }
1037 }
1038
1039 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
1040 {
1041     if (debuggerCore()->boolSetting(VerboseLog))
1042         str << blankSeparator << "-v";
1043     if (debuggerCore()->boolSetting(UseDebuggingHelpers))
1044         str << blankSeparator << "-c";
1045     const QByteArray typeFormats = watchHandler()->typeFormatRequests();
1046     if (!typeFormats.isEmpty())
1047         str << blankSeparator << "-T " << typeFormats;
1048     const QByteArray individualFormats = watchHandler()->individualFormatRequests();
1049     if (!individualFormats.isEmpty())
1050         str << blankSeparator << "-I " << individualFormats;
1051 }
1052
1053 void CdbEngine::updateLocalVariable(const QByteArray &iname)
1054 {
1055     const bool isWatch = isWatchIName(iname);
1056     if (debugWatches)
1057         qDebug() << "updateLocalVariable watch=" << isWatch << iname;
1058     QByteArray localsArguments;
1059     ByteArrayInputStream str(localsArguments);
1060     addLocalsOptions(str);
1061     if (!isWatch) {
1062         const int stackFrame = stackHandler()->currentIndex();
1063         if (stackFrame < 0) {
1064             qWarning("Internal error; no stack frame in updateLocalVariable");
1065             return;
1066         }
1067         str << blankSeparator << stackFrame;
1068     }
1069     str << blankSeparator << iname;
1070     postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals);
1071 }
1072
1073 unsigned CdbEngine::debuggerCapabilities() const
1074 {
1075     return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
1076            |WatchpointByAddressCapability|JumpToLineCapability|AddWatcherCapability
1077            |ReloadModuleCapability
1078            |BreakOnThrowAndCatchCapability // Sort-of: Can break on throw().
1079            |BreakConditionCapability|TracePointCapability
1080            |BreakModuleCapability
1081            |OperateByInstructionCapability
1082            |RunToLineCapability;
1083 }
1084
1085 void CdbEngine::executeStep()
1086 {
1087     if (!m_operateByInstruction)
1088         m_sourceStepInto = true; // See explanation at handleStackTrace().
1089     postCommand(QByteArray("t"), 0); // Step into-> t (trace)
1090     STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1091     notifyInferiorRunRequested();
1092 }
1093
1094 void CdbEngine::executeStepOut()
1095 {
1096     postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
1097     STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1098     notifyInferiorRunRequested();
1099 }
1100
1101 void CdbEngine::executeNext()
1102 {
1103     postCommand(QByteArray("p"), 0); // Step over -> p
1104     STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1105     notifyInferiorRunRequested();
1106 }
1107
1108 void CdbEngine::executeStepI()
1109 {
1110     executeStep();
1111 }
1112
1113 void CdbEngine::executeNextI()
1114 {
1115     executeNext();
1116 }
1117
1118 void CdbEngine::continueInferior()
1119 {
1120     STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1121     notifyInferiorRunRequested();
1122     doContinueInferior();
1123 }
1124
1125 void CdbEngine::doContinueInferior()
1126 {
1127     postCommand(QByteArray("g"), 0);
1128 }
1129
1130 bool CdbEngine::canInterruptInferior() const
1131 {
1132     return m_effectiveStartMode != AttachToRemote && inferiorPid();
1133 }
1134
1135 void CdbEngine::interruptInferior()
1136 {
1137     if (debug)
1138         qDebug() << "CdbEngine::interruptInferior()" << stateName(state());
1139     if (canInterruptInferior()) {
1140         doInterruptInferior(NoSpecialStop);
1141     } else {
1142         showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
1143         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
1144         notifyInferiorStopOk();
1145         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
1146         notifyInferiorRunRequested();
1147         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
1148         notifyInferiorRunOk();
1149     }
1150 }
1151
1152 void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
1153 {
1154     if (m_specialStopMode == NoSpecialStop)
1155         doInterruptInferior(CustomSpecialStop);
1156     m_customSpecialStopData.push_back(v);
1157 }
1158
1159 void CdbEngine::doInterruptInferior(SpecialStopMode sm)
1160 {
1161 #ifdef Q_OS_WIN
1162     const SpecialStopMode oldSpecialMode = m_specialStopMode;
1163     m_specialStopMode = sm;
1164     QString errorMessage;
1165     showMessage(QString::fromLatin1("Interrupting process %1...").arg(inferiorPid()), LogMisc);
1166     if (!winDebugBreakProcess(inferiorPid(), &errorMessage)) {
1167         m_specialStopMode = oldSpecialMode;
1168         showMessage(QString::fromLatin1("Cannot interrupt process %1: %2").arg(inferiorPid()).arg(errorMessage), LogError);
1169     }
1170 #else
1171     Q_UNUSED(sm)
1172 #endif
1173 }
1174
1175 void CdbEngine::executeRunToLine(const ContextData &data)
1176 {
1177     // Add one-shot breakpoint
1178     BreakpointParameters bp;
1179     if (data.address) {
1180         bp.type =BreakpointByAddress;
1181         bp.address = data.address;
1182     } else {
1183         bp.type =BreakpointByFileAndLine;
1184         bp.fileName = data.fileName;
1185         bp.lineNumber = data.lineNumber;
1186     }
1187     postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1188     continueInferior();
1189 }
1190
1191 void CdbEngine::executeRunToFunction(const QString &functionName)
1192 {
1193     // Add one-shot breakpoint
1194     BreakpointParameters bp(BreakpointByFunction);
1195     bp.functionName = functionName;
1196
1197     postCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointId(-1), true), 0);
1198     continueInferior();
1199 }
1200
1201 void CdbEngine::setRegisterValue(int regnr, const QString &value)
1202 {
1203     const Registers registers = registerHandler()->registers();
1204     QTC_ASSERT(regnr < registers.size(), return)
1205     // Value is decimal or 0x-hex-prefixed
1206     QByteArray cmd;
1207     ByteArrayInputStream str(cmd);
1208     str << "r " << registers.at(regnr).name << '=' << value;
1209     postCommand(cmd, 0);
1210     reloadRegisters();
1211 }
1212
1213 void CdbEngine::executeJumpToLine(const ContextData &data)
1214 {
1215     if (data.address) {
1216         // Goto address directly.
1217         jumpToAddress(data.address);
1218         gotoLocation(Location(data.address));
1219     } else {
1220         // Jump to source line: Resolve source line address and go to that location
1221         QByteArray cmd;
1222         ByteArrayInputStream str(cmd);
1223         str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
1224         const QVariant cookie = qVariantFromValue(data);
1225         postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
1226     }
1227 }
1228
1229 void CdbEngine::jumpToAddress(quint64 address)
1230 {
1231     // Fake a jump to address by setting the PC register.
1232     QByteArray registerCmd;
1233     ByteArrayInputStream str(registerCmd);
1234     // PC-register depending on 64/32bit.
1235     str << "r " << (startParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '=';
1236     str.setHexPrefix(true);
1237     str.setIntegerBase(16);
1238     str << address;
1239     postCommand(registerCmd, 0);
1240 }
1241
1242 void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
1243 {
1244     if (cmd->reply.isEmpty())
1245         return;
1246     // Evaluate expression: 5365511549 = 00000001`3fcf357d
1247     // Set register 'rip' to hex address and goto lcoation
1248     QString answer = QString::fromAscii(cmd->reply.front()).trimmed();
1249     const int equalPos = answer.indexOf(" = ");
1250     if (equalPos == -1)
1251         return;
1252     answer.remove(0, equalPos + 3);
1253     answer.remove(QLatin1Char('`'));
1254     bool ok;
1255     const quint64 address = answer.toLongLong(&ok, 16);
1256     if (ok && address) {
1257         QTC_ASSERT(qVariantCanConvert<ContextData>(cmd->cookie), return);
1258         const ContextData cookie = qvariant_cast<ContextData>(cmd->cookie);
1259         jumpToAddress(address);
1260         gotoLocation(Location(cookie.fileName, cookie.lineNumber));
1261     }
1262 }
1263
1264 static inline bool isAsciiWord(const QString &s)
1265 {
1266     foreach (const QChar &c, s) {
1267         if (!c.isLetterOrNumber() || c.toAscii() == 0)
1268             return false;
1269     }
1270     return true;
1271 }
1272
1273 void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
1274 {
1275     if (debug)
1276         qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;
1277
1278     if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
1279         qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
1280         return;
1281     }
1282     QByteArray cmd;
1283     ByteArrayInputStream str(cmd);
1284     switch (value.type()) {
1285     case QVariant::String: {
1286         // Convert qstring to Utf16 data not considering endianness for Windows.
1287         const QString s = value.toString();
1288         if (isAsciiWord(s)) {
1289             str << m_extensionCommandPrefixBA << "assign \"" << w->iname << '='
1290                 << s.toLatin1() << '"';
1291         } else {
1292             const QByteArray utf16(reinterpret_cast<const char *>(s.utf16()), 2 * s.size());
1293             str << m_extensionCommandPrefixBA << "assign -u " << w->iname << '='
1294                 << utf16.toHex();
1295         }
1296     }
1297         break;
1298     default:
1299         str << m_extensionCommandPrefixBA << "assign " << w->iname << '='
1300             << value.toString();
1301         break;
1302     }
1303
1304     postCommand(cmd, 0);
1305     // Update all locals in case we change a union or something pointed to
1306     // that affects other variables, too.
1307     updateLocals();
1308 }
1309
1310 void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
1311 {
1312     int currentThreadId;
1313     Threads threads = ThreadsHandler::parseGdbmiThreads(data, &currentThreadId);
1314     threadsHandler()->setThreads(threads);
1315     threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
1316                                          forceCurrentThreadId : currentThreadId);
1317 }
1318
1319 void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
1320 {
1321     if (debug)
1322         qDebug("CdbEngine::handleThreads success=%d", reply->success);
1323     if (reply->success) {
1324         GdbMi data;
1325         data.fromString(reply->reply);
1326         parseThreads(data);
1327         // Continue sequence
1328         postCommandSequence(reply->commandSequence);
1329     } else {
1330         showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1331     }
1332 }
1333
1334 void CdbEngine::executeDebuggerCommand(const QString &command)
1335 {
1336     postCommand(command.toLocal8Bit(), QuietCommand);
1337 }
1338
1339 // Post command without callback
1340 void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
1341 {
1342     if (debug)
1343         qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
1344                elapsedLogTime(), cmd.constData(), flags, stateName(state()));
1345     if (!(flags & QuietCommand))
1346         showMessage(QString::fromLocal8Bit(cmd), LogInput);
1347     m_process.write(cmd + '\n');
1348 }
1349
1350 // Post a built-in-command producing free-format output with a callback.
1351 // In order to catch the output, it is enclosed in 'echo' commands
1352 // printing a specially formatted token to be identifiable in the output.
1353 void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
1354                                    BuiltinCommandHandler handler,
1355                                    unsigned nextCommandFlag,
1356                                    const QVariant &cookie)
1357 {
1358     if (!m_accessible) {
1359         const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
1360                 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1361         showMessage(msg, LogError);
1362         return;
1363     }
1364     if (!flags & QuietCommand)
1365         showMessage(QString::fromLocal8Bit(cmd), LogInput);
1366
1367     const int token = m_nextCommandToken++;
1368     CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));
1369
1370     m_builtinCommandQueue.push_back(pendingCommand);
1371     // Enclose command in echo-commands for token
1372     QByteArray fullCmd;
1373     ByteArrayInputStream str(fullCmd);
1374     str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
1375             << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
1376     if (debug)
1377         qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1378                elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1379                m_builtinCommandQueue.size(), nextCommandFlag);
1380     if (debug > 1)
1381         qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
1382                fullCmd.constData());
1383     m_process.write(fullCmd);
1384 }
1385
1386 // Post an extension command producing one-line output with a callback,
1387 // pass along token for identification in queue.
1388 void CdbEngine::postExtensionCommand(const QByteArray &cmd,
1389                                      const QByteArray &arguments,
1390                                      unsigned flags,
1391                                      ExtensionCommandHandler handler,
1392                                      unsigned nextCommandFlag,
1393                                      const QVariant &cookie)
1394 {
1395     if (!m_accessible) {
1396         const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
1397                 .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
1398         showMessage(msg, LogError);
1399         return;
1400     }
1401
1402     const int token = m_nextCommandToken++;
1403
1404     // Format full command with token to be recognizeable in the output
1405     QByteArray fullCmd;
1406     ByteArrayInputStream str(fullCmd);
1407     str << m_extensionCommandPrefixBA << cmd << " -t " << token;
1408     if (!arguments.isEmpty())
1409         str <<  ' ' << arguments;
1410
1411     if (!flags & QuietCommand)
1412         showMessage(QString::fromLocal8Bit(fullCmd), LogInput);
1413
1414     CdbExtensionCommandPtr pendingCommand(new CdbExtensionCommand(fullCmd, token, flags, handler, nextCommandFlag, cookie));
1415
1416     m_extensionCommandQueue.push_back(pendingCommand);
1417     // Enclose command in echo-commands for token
1418     if (debug)
1419         qDebug("CdbEngine::postExtensionCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
1420                elapsedLogTime(), fullCmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
1421                m_extensionCommandQueue.size(), nextCommandFlag);
1422     m_process.write(fullCmd + '\n');
1423 }
1424
1425 void CdbEngine::activateFrame(int index)
1426 {
1427     // TODO: assembler,etc
1428     if (index < 0)
1429         return;
1430     const StackFrames &frames = stackHandler()->frames();
1431     QTC_ASSERT(index < frames.size(), return; )
1432
1433     const StackFrame frame = frames.at(index);
1434     if (debug || debugLocals)
1435         qDebug("activateFrame idx=%d '%s' %d", index,
1436                qPrintable(frame.file), frame.line);
1437     stackHandler()->setCurrentIndex(index);
1438     const bool showAssembler = !frames.at(index).isUsable();
1439     if (showAssembler) { // Assembly code: Clean out model and force instruction mode.
1440         watchHandler()->beginCycle();
1441         watchHandler()->endCycle();
1442         QAction *assemblerAction = theAssemblerAction();
1443         if (assemblerAction->isChecked()) {
1444             gotoLocation(frame);
1445         } else {
1446             assemblerAction->trigger(); // Seems to trigger update
1447         }
1448     } else {
1449         gotoLocation(frame);
1450         updateLocals(true);
1451     }
1452 }
1453
1454 void CdbEngine::updateLocals(bool forNewStackFrame)
1455 {
1456     typedef QHash<QByteArray, int> WatcherHash;
1457
1458     const int frameIndex = stackHandler()->currentIndex();
1459     if (frameIndex < 0) {
1460         watchHandler()->beginCycle();
1461         watchHandler()->endCycle();
1462         return;
1463     }
1464     const StackFrame frame = stackHandler()->currentFrame();
1465     if (!frame.isUsable()) {
1466         watchHandler()->beginCycle();
1467         watchHandler()->endCycle();
1468         return;
1469     }
1470     /* Watchers: Forcibly discard old symbol group as switching from
1471      * thread 0/frame 0 -> thread 1/assembly -> thread 0/frame 0 will otherwise re-use it
1472      * and cause errors as it seems to go 'stale' when switching threads.
1473      * Initial expand, get uninitialized and query */
1474     QByteArray arguments;
1475     ByteArrayInputStream str(arguments);
1476     str << "-D";
1477     // Pre-expand
1478     const QSet<QByteArray> expanded = watchHandler()->expandedINames();
1479     if (!expanded.isEmpty()) {
1480         str << blankSeparator << "-e ";
1481         int i = 0;
1482         foreach(const QByteArray &e, expanded) {
1483             if (i++)
1484                 str << ',';
1485             str << e;
1486         }
1487     }
1488     addLocalsOptions(str);
1489     // Uninitialized variables if desired. Quote as safeguard against shadowed
1490     // variables in case of errors in uninitializedVariables().
1491     if (debuggerCore()->boolSetting(UseCodeModel)) {
1492         QStringList uninitializedVariables;
1493         getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
1494                                   frame.function, frame.file, frame.line, &uninitializedVariables);
1495         if (!uninitializedVariables.isEmpty()) {
1496             str << blankSeparator << "-u \"";
1497             int i = 0;
1498             foreach(const QString &u, uninitializedVariables) {
1499                 if (i++)
1500                     str << ',';
1501                 str << localsPrefixC << u;
1502             }
1503             str << '"';
1504         }
1505     }
1506     // Perform watches synchronization
1507     str << blankSeparator << "-W";
1508     const WatcherHash watcherHash = WatchHandler::watcherNames();
1509     if (!watcherHash.isEmpty()) {
1510         const WatcherHash::const_iterator cend = watcherHash.constEnd();
1511         for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) {
1512             str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"';
1513         }
1514     }
1515
1516     // Required arguments: frame
1517     str << blankSeparator << frameIndex;
1518     watchHandler()->beginCycle();
1519     postExtensionCommand("locals", arguments, 0, &CdbEngine::handleLocals, 0, QVariant(forNewStackFrame));
1520 }
1521
1522 void CdbEngine::selectThread(int index)
1523 {
1524     if (index < 0 || index == threadsHandler()->currentThread())
1525         return;
1526
1527     resetLocation();
1528     const int newThreadId = threadsHandler()->threads().at(index).id;
1529     threadsHandler()->setCurrentThread(index);
1530
1531     const QByteArray cmd = "~" + QByteArray::number(newThreadId) + " s";
1532     postBuiltinCommand(cmd, 0, &CdbEngine::dummyHandler, CommandListStack);
1533 }
1534
1535 void CdbEngine::fetchDisassembler(DisassemblerAgent *agent)
1536 {
1537     QTC_ASSERT(m_accessible, return;)
1538     QByteArray cmd;
1539     ByteArrayInputStream str(cmd);
1540     str <<  "u " << hex << hexPrefixOn << agent->address() << " L40";
1541     const QVariant cookie = qVariantFromValue<DisassemblerAgent*>(agent);
1542     postBuiltinCommand(cmd, 0, &CdbEngine::handleDisassembler, 0, cookie);
1543 }
1544
1545 // Parse: "00000000`77606060 cc              int     3"
1546 void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
1547 {
1548     QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
1549     DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
1550     agent->setContents(parseCdbDisassembler(command->reply));
1551 }
1552
1553 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
1554 {
1555     if (debug)
1556         qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
1557     const MemoryViewCookie cookie(agent, editor, addr, length);
1558     if (m_accessible) {
1559         postFetchMemory(cookie);
1560     } else {
1561         doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1562     }
1563 }
1564
1565 void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
1566 {
1567     QByteArray args;
1568     ByteArrayInputStream str(args);
1569     str << cookie.address << ' ' << cookie.length;
1570     postExtensionCommand("memory", args, 0, &CdbEngine::handleMemory, 0,
1571                          qVariantFromValue(cookie));
1572 }
1573
1574 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
1575 {
1576     QTC_ASSERT(!data.isEmpty(), return; )
1577     if (!m_accessible) {
1578         const MemoryChangeCookie cookie(addr, data);
1579         doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
1580     } else {
1581         postCommand(cdbWriteMemoryCommand(addr, data), 0);
1582     }
1583 }
1584
1585 void CdbEngine::handleMemory(const CdbExtensionCommandPtr &command)
1586 {
1587     QTC_ASSERT(qVariantCanConvert<MemoryViewCookie>(command->cookie), return;)
1588     const MemoryViewCookie memViewCookie = qvariant_cast<MemoryViewCookie>(command->cookie);
1589     if (command->success) {
1590         const QByteArray data = QByteArray::fromBase64(command->reply);
1591         if (unsigned(data.size()) == memViewCookie.length)
1592             memViewCookie.agent->addLazyData(memViewCookie.editorToken,
1593                                              memViewCookie.address, data);
1594     } else {
1595         showMessage(QString::fromLocal8Bit(command->errorMessage), LogWarning);
1596     }
1597 }
1598
1599 void CdbEngine::reloadModules()
1600 {
1601     postCommandSequence(CommandListModules);
1602 }
1603
1604 void CdbEngine::loadSymbols(const QString & /* moduleName */)
1605 {
1606 }
1607
1608 void CdbEngine::loadAllSymbols()
1609 {
1610 }
1611
1612 void CdbEngine::requestModuleSymbols(const QString &moduleName)
1613 {
1614     Q_UNUSED(moduleName)
1615 }
1616
1617 void CdbEngine::reloadRegisters()
1618 {
1619     postCommandSequence(CommandListRegisters);
1620 }
1621
1622 void CdbEngine::reloadSourceFiles()
1623 {
1624 }
1625
1626 void CdbEngine::reloadFullStack()
1627 {
1628     if (debug)
1629         qDebug("%s", Q_FUNC_INFO);
1630     postCommandSequence(CommandListStack);
1631 }
1632
1633 void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply)
1634 {
1635     if (reply->success) {
1636         notifyInferiorPid(reply->reply.toULongLong());
1637         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
1638         notifyInferiorSetupOk();
1639     }  else {
1640         showMessage(QString::fromLatin1("Failed to determine inferior pid: %1").
1641                     arg(QLatin1String(reply->errorMessage)), LogError);
1642         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
1643         notifyInferiorSetupFailed();
1644     }
1645 }
1646
1647 // Parse CDB gdbmi register syntax
1648 static inline Register parseRegister(const GdbMi &gdbmiReg)
1649 {
1650     Register reg;
1651     reg.name = gdbmiReg.findChild("name").data();
1652     const GdbMi description = gdbmiReg.findChild("description");
1653     if (description.type() != GdbMi::Invalid) {
1654         reg.name += " (";
1655         reg.name += description.data();
1656         reg.name += ')';
1657     }
1658     reg.value = QString::fromAscii(gdbmiReg.findChild("value").data());
1659     return reg;
1660 }
1661
1662 void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
1663 {
1664     if (reply->success) {
1665         GdbMi value;
1666         value.fromString(reply->reply);
1667         if (value.type() == GdbMi::List) {
1668             Modules modules;
1669             modules.reserve(value.childCount());
1670             foreach (const GdbMi &gdbmiModule, value.children()) {
1671                 Module module;
1672                 module.moduleName = QString::fromAscii(gdbmiModule.findChild("name").data());
1673                 module.modulePath = QString::fromAscii(gdbmiModule.findChild("image").data());
1674                 module.startAddress = gdbmiModule.findChild("start").data().toULongLong(0, 0);
1675                 module.endAddress = gdbmiModule.findChild("end").data().toULongLong(0, 0);
1676                 if (gdbmiModule.findChild("deferred").type() == GdbMi::Invalid)
1677                     module.symbolsRead = Module::ReadOk;
1678                 modules.push_back(module);
1679             }
1680             modulesHandler()->setModules(modules);
1681         } else {
1682             showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
1683             qWarning("Parse error in modules response:\n%s", reply->reply.constData());
1684         }
1685     }  else {
1686         showMessage(QString::fromLatin1("Failed to determine modules: %1").
1687                     arg(QLatin1String(reply->errorMessage)), LogError);
1688     }
1689     postCommandSequence(reply->commandSequence);
1690
1691 }
1692
1693 void CdbEngine::handleRegisters(const CdbExtensionCommandPtr &reply)
1694 {
1695     if (reply->success) {
1696         GdbMi value;
1697         value.fromString(reply->reply);
1698         if (value.type() == GdbMi::List) {
1699             Registers registers;
1700             registers.reserve(value.childCount());
1701             foreach (const GdbMi &gdbmiReg, value.children())
1702                 registers.push_back(parseRegister(gdbmiReg));
1703             registerHandler()->setAndMarkRegisters(registers);
1704         } else {
1705             showMessage(QString::fromLatin1("Parse error in registers response."), LogError);
1706             qWarning("Parse error in registers response:\n%s", reply->reply.constData());
1707         }
1708     }  else {
1709         showMessage(QString::fromLatin1("Failed to determine registers: %1").
1710                     arg(QLatin1String(reply->errorMessage)), LogError);
1711     }
1712     postCommandSequence(reply->commandSequence);
1713 }
1714
1715 void CdbEngine::handleLocals(const CdbExtensionCommandPtr &reply)
1716 {
1717     if (reply->success) {
1718         QList<WatchData> watchData;
1719         GdbMi root;
1720         root.fromString(reply->reply);
1721         QTC_ASSERT(root.isList(), return ; )
1722         if (debugLocals) {
1723             qDebug() << root.toString(true, 4);
1724         }
1725         // Courtesy of GDB engine
1726         foreach (const GdbMi &child, root.children()) {
1727             WatchData dummy;
1728             dummy.iname = child.findChild("iname").data();
1729             dummy.name = QLatin1String(child.findChild("name").data());
1730             parseWatchData(watchHandler()->expandedINames(), dummy, child, &watchData);
1731         }
1732         watchHandler()->insertBulkData(watchData);
1733         watchHandler()->endCycle();
1734         if (debugLocals) {
1735             QDebug nsp = qDebug().nospace();
1736             nsp << "Obtained " << watchData.size() << " items:\n";
1737             foreach (const WatchData &wd, watchData)
1738                 nsp << wd.toString() <<'\n';
1739         }
1740         const bool forNewStackFrame = reply->cookie.toBool();
1741         if (forNewStackFrame)
1742             emit stackFrameCompleted();
1743     } else {
1744         showMessage(QString::fromLatin1(reply->errorMessage), LogWarning);
1745     }
1746 }
1747
1748 void CdbEngine::handleExpandLocals(const CdbExtensionCommandPtr &reply)
1749 {
1750     if (!reply->success)
1751         showMessage(QString::fromLatin1(reply->errorMessage), LogError);
1752 }
1753
1754 enum CdbExecutionStatus {
1755 CDB_STATUS_NO_CHANGE=0, CDB_STATUS_GO = 1, CDB_STATUS_GO_HANDLED = 2,
1756 CDB_STATUS_GO_NOT_HANDLED = 3, CDB_STATUS_STEP_OVER = 4,
1757 CDB_STATUS_STEP_INTO = 5, CDB_STATUS_BREAK = 6, CDB_STATUS_NO_DEBUGGEE = 7,
1758 CDB_STATUS_STEP_BRANCH = 8, CDB_STATUS_IGNORE_EVENT = 9,
1759 CDB_STATUS_RESTART_REQUESTED = 10, CDB_STATUS_REVERSE_GO = 11,
1760 CDB_STATUS_REVERSE_STEP_BRANCH = 12, CDB_STATUS_REVERSE_STEP_OVER = 13,
1761 CDB_STATUS_REVERSE_STEP_INTO = 14 };
1762
1763 static const char *cdbStatusName(unsigned long s)
1764 {
1765     switch (s) {
1766     case CDB_STATUS_NO_CHANGE:
1767         return "No change";
1768     case CDB_STATUS_GO:
1769         return "go";
1770     case CDB_STATUS_GO_HANDLED:
1771         return "go_handled";
1772     case CDB_STATUS_GO_NOT_HANDLED:
1773         return "go_not_handled";
1774     case CDB_STATUS_STEP_OVER:
1775         return "step_over";
1776     case CDB_STATUS_STEP_INTO:
1777         return "step_into";
1778     case CDB_STATUS_BREAK:
1779         return "break";
1780     case CDB_STATUS_NO_DEBUGGEE:
1781         return "no_debuggee";
1782     case CDB_STATUS_STEP_BRANCH:
1783         return "step_branch";
1784     case CDB_STATUS_IGNORE_EVENT:
1785         return "ignore_event";
1786     case CDB_STATUS_RESTART_REQUESTED:
1787         return "restart_requested";
1788     case CDB_STATUS_REVERSE_GO:
1789         return "reverse_go";
1790     case CDB_STATUS_REVERSE_STEP_BRANCH:
1791         return "reverse_step_branch";
1792     case CDB_STATUS_REVERSE_STEP_OVER:
1793         return "reverse_step_over";
1794     case CDB_STATUS_REVERSE_STEP_INTO:
1795         return "reverse_step_into";
1796     }
1797     return "unknown";
1798 }
1799
1800 /* Examine how to react to a stop. */
1801 enum StopActionFlags
1802 {
1803     // Report options
1804     StopReportLog = 0x1,
1805     StopReportStatusMessage = 0x2,
1806     StopReportParseError = 0x4,
1807     StopShowExceptionMessageBox = 0x8,
1808     // Notify stop or just continue
1809     StopNotifyStop = 0x10,
1810     StopIgnoreContinue = 0x20,
1811     // Hit on break in artificial stop thread (created by DebugBreak()).
1812     StopInArtificialThread = 0x40,
1813     StopShutdownInProgress = 0x80 // Shutdown in progress
1814 };
1815
1816 static inline QString msgTracePointTriggered(BreakpointId id, const int number,
1817                                              const QString &threadId)
1818 {
1819     return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
1820             .arg(id).arg(number).arg(threadId);
1821 }
1822
1823 static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
1824                                                        const QByteArray &condition,
1825                                                        const QString &threadId)
1826 {
1827     return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
1828             .arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
1829 }
1830
1831 unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
1832                                       QString *message,
1833                                       QString *exceptionBoxMessage,
1834                                       bool conditionalBreakPointTriggered)
1835 {
1836     // Report stop reason (GDBMI)
1837     unsigned rc  = 0;
1838     if (targetState() == DebuggerFinished)
1839         rc |= StopShutdownInProgress;
1840     if (debug)
1841         qDebug("%s", stopReason.toString(true, 4).constData());
1842     const QByteArray reason = stopReason.findChild("reason").data();
1843     if (reason.isEmpty()) {
1844         *message = tr("Malformed stop response received.");
1845         rc |= StopReportParseError|StopNotifyStop;
1846         return rc;
1847     }
1848     // Additional stop messages occurring for debuggee function calls (widgetAt, etc). Just log.
1849     if (state() == InferiorStopOk) {
1850         *message = QString::fromLatin1("Ignored stop notification from function call (%1).").
1851                     arg(QString::fromAscii(reason));
1852         rc |= StopReportLog;
1853         return rc;
1854     }
1855     const int threadId = stopReason.findChild("threadId").data().toInt();
1856     if (reason == "breakpoint") {
1857         // Note: Internal breakpoints (run to line) are reported with id=0.
1858         // Step out creates temporary breakpoints with id 10000.
1859         BreakpointId id = 0;
1860         int number = 0;
1861         const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
1862         if (breakpointIdG.isValid()) {
1863             id = breakpointIdG.data().toULongLong();
1864             if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
1865                 const BreakpointResponse parameters =  breakHandler()->response(id);
1866                 // Trace point? Just report.
1867                 number = parameters.number;
1868                 if (parameters.tracepoint) {
1869                     *message = msgTracePointTriggered(id, number, QString::number(threadId));
1870                     return StopReportLog|StopIgnoreContinue;
1871                 }
1872                 // Trigger evaluation of BP expression unless we are already in the response.
1873                 if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) {
1874                     *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition,
1875                                                                 QString::number(threadId));
1876                     ConditionalBreakPointCookie cookie(id);
1877                     cookie.stopReason = stopReason;
1878                     evaluateExpression(parameters.condition, qVariantFromValue(cookie));
1879                     return StopReportLog;
1880                 }
1881             } else {
1882                 id = 0;
1883             }
1884         }
1885         QString tid = QString::number(threadId);
1886         if (id && breakHandler()->type(id) == WatchpointAtAddress) {
1887             *message = msgWatchpointByAddressTriggered(id, number, breakHandler()->address(id), tid);
1888         } else if (id && breakHandler()->type(id) == WatchpointAtExpression) {
1889             *message = msgWatchpointByExpressionTriggered(id, number, breakHandler()->expression(id), tid);
1890         } else {
1891             *message = msgBreakpointTriggered(id, number, tid);
1892         }
1893         rc |= StopReportStatusMessage|StopNotifyStop;
1894         return rc;
1895     }
1896     if (reason == "exception") {
1897         WinException exception;
1898         exception.fromGdbMI(stopReason);
1899         QString description = exception.toString();
1900 #ifdef Q_OS_WIN
1901         // It is possible to hit on a startup trap or WOW86 exception while stepping (if something
1902         // pulls DLLs. Avoid showing a 'stopped' Message box.
1903         if (exception.exceptionCode == winExceptionStartupCompleteTrap
1904             || exception.exceptionCode == winExceptionWX86Breakpoint)
1905             return StopNotifyStop;
1906         if (exception.exceptionCode == winExceptionCtrlPressed) {
1907             // Detect interruption by pressing Ctrl in a console and force a switch to thread 0.
1908             *message = msgInterrupted();
1909             rc |= StopReportStatusMessage|StopNotifyStop|StopInArtificialThread;
1910             return rc;
1911         }
1912         if (isDebuggerWinException(exception.exceptionCode)) {
1913             rc |= StopReportStatusMessage|StopNotifyStop;
1914             // Detect interruption by DebugBreak() and force a switch to thread 0.
1915             if (exception.function == "ntdll!DbgBreakPoint")
1916                 rc |= StopInArtificialThread;
1917             *message = msgInterrupted();
1918             return rc;
1919         }
1920 #endif
1921         *exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
1922         *message = description;
1923         rc |= StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
1924         return rc;
1925     }
1926     *message = msgStopped(QLatin1String(reason));
1927     rc |= StopReportStatusMessage|StopNotifyStop;
1928     return rc;
1929 }
1930
1931 void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
1932 {
1933     if (!m_hasDebuggee)
1934         return;
1935
1936     if (debug)
1937         qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
1938                elapsedLogTime(), messageBA.constData(),
1939                stateName(state()), m_specialStopMode);
1940
1941     // Switch source level debugging
1942     syncOperateByInstruction(m_operateByInstructionPending);
1943
1944     // Engine-special stop reasons: Breakpoints and setup
1945     const SpecialStopMode specialStopMode =  m_specialStopMode;
1946
1947     m_specialStopMode = NoSpecialStop;
1948
1949     switch(specialStopMode) {
1950     case SpecialStopSynchronizeBreakpoints:
1951         if (debug)
1952             qDebug("attemptBreakpointSynchronization in special stop");
1953         attemptBreakpointSynchronization();
1954         doContinueInferior();
1955         return;
1956     case SpecialStopGetWidgetAt:
1957         postWidgetAtCommand();
1958         return;
1959     case CustomSpecialStop:
1960         foreach (const QVariant &data, m_customSpecialStopData)
1961             handleCustomSpecialStop(data);
1962         m_customSpecialStopData.clear();
1963         doContinueInferior();
1964         return;
1965     case NoSpecialStop:
1966         break;
1967     }
1968
1969     if (state() == EngineSetupRequested) { // Temporary stop at beginning
1970         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
1971         notifyEngineSetupOk();
1972         return;
1973     }
1974     GdbMi stopReason;
1975     stopReason.fromString(messageBA);
1976     processStop(stopReason, false);
1977 }
1978
1979 void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointTriggered)
1980 {
1981     // Further examine stop and report to user
1982     QString message;
1983     QString exceptionBoxMessage;
1984     int forcedThreadId = -1;
1985     const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
1986                                                  conditionalBreakPointTriggered);
1987     // Do the non-blocking log reporting
1988     if (stopFlags & StopReportLog)
1989         showMessage(message, LogMisc);
1990     if (stopFlags & StopReportStatusMessage)
1991         showStatusMessage(message);
1992     if (stopFlags & StopReportParseError)
1993         showMessage(message, LogError);
1994     // Ignore things like WOW64, report tracepoints.
1995     if (stopFlags & StopIgnoreContinue) {
1996         postCommand("g", 0);
1997         return;
1998     }
1999     // Notify about state and send off command sequence to get stack, etc.
2000     if (stopFlags & StopNotifyStop) {
2001         if (state() == InferiorStopRequested) {
2002             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk")
2003             notifyInferiorStopOk();
2004         } else {
2005             STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
2006             notifyInferiorSpontaneousStop();
2007         }
2008         // Prevent further commands from being sent if shutdown is in progress
2009         if (stopFlags & StopShutdownInProgress) {
2010             showMessage(QString::fromLatin1("Shutdown request detected..."));
2011             return;
2012         }
2013         const bool sourceStepInto = m_sourceStepInto;
2014         m_sourceStepInto = false;
2015         // Start sequence to get all relevant data.
2016         if (stopFlags & StopInArtificialThread) {
2017             showMessage(tr("Switching to main thread..."), LogMisc);
2018             postCommand("~0 s", 0);
2019             forcedThreadId = 0;
2020             // Re-fetch stack again.
2021             postCommandSequence(CommandListStack);
2022         } else {
2023             const GdbMi stack = stopReason.findChild("stack");
2024             if (stack.isValid()) {
2025                 if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
2026                     executeStep(); // Hit on a frame while step into, see parseStackTrace().
2027                     return;
2028                 }
2029             } else {
2030                 showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
2031             }
2032         }
2033         const GdbMi threads = stopReason.findChild("threads");
2034         if (threads.isValid()) {
2035             parseThreads(threads, forcedThreadId);
2036         } else {
2037             showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
2038         }
2039         // Fire off remaining commands asynchronously
2040         if (!m_pendingBreakpointMap.isEmpty())
2041             postCommandSequence(CommandListBreakPoints);
2042         if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
2043             postCommandSequence(CommandListRegisters);
2044         if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
2045             postCommandSequence(CommandListModules);
2046     }
2047     // After the sequence has been sent off and CDB is pondering the commands,
2048     // pop up a message box for exceptions.
2049     if (stopFlags & StopShowExceptionMessageBox)
2050         showStoppedByExceptionMessageBox(exceptionBoxMessage);
2051 }
2052
2053 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
2054 {
2055     const DebuggerState s = state();
2056     if (!m_hasDebuggee || s == InferiorRunOk) // suppress reports
2057         return;
2058
2059     if (debug)
2060         qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
2061                elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2062
2063     switch(s) {
2064     case EngineShutdownRequested:
2065         shutdownEngine();
2066         break;
2067     case InferiorShutdownRequested:
2068         shutdownInferior();
2069         break;
2070     default:
2071         break;
2072     }
2073 }
2074
2075 void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
2076 {
2077     const DebuggerState s = state();
2078
2079     // suppress reports
2080     if (!m_hasDebuggee || (s == InferiorRunOk && cdbExState != CDB_STATUS_NO_DEBUGGEE))
2081         return;
2082
2083     if (debug)
2084         qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
2085                elapsedLogTime(), cdbStatusName(cdbExState), stateName(state()), m_specialStopMode);
2086
2087     switch (state()) {
2088     case EngineSetupRequested:
2089         break;
2090     case EngineRunRequested:
2091         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk")
2092         notifyEngineRunAndInferiorRunOk();
2093         break;
2094     case InferiorRunOk:
2095     case InferiorStopOk:
2096         // Inaccessible without debuggee (exit breakpoint)
2097         // We go for spontaneous engine shutdown instead.
2098         if (cdbExState == CDB_STATUS_NO_DEBUGGEE) {
2099             if (debug)
2100                 qDebug("Lost debuggeee");
2101             m_hasDebuggee = false;
2102         }
2103         break;
2104     case InferiorRunRequested:
2105         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk")
2106         notifyInferiorRunOk();
2107         resetLocation();
2108         break;
2109     case EngineShutdownRequested:
2110         break;
2111     default:
2112         break;
2113     }
2114 }
2115
2116 void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message)
2117 {
2118     if (debug > 1) {
2119         QDebug nospace = qDebug().nospace();
2120         nospace << "handleExtensionMessage " << t << ' ' << token << ' ' << what
2121                 << ' ' << stateName(state());
2122         if (t == 'N' || debug > 1) {
2123             nospace << ' ' << message;
2124         } else {
2125             nospace << ' ' << message.size() << " bytes";
2126         }
2127     }
2128
2129     // Is there a reply expected, some command queued?
2130     if (t == 'R' || t == 'N') {
2131         if (token == -1) { // Default token, user typed in extension command
2132             showMessage(QString::fromLatin1(message), LogMisc);
2133             return;
2134         }
2135         const int index = indexOfCommand(m_extensionCommandQueue, token);
2136         if (index != -1) {
2137             // Did the command finish? Take off queue and complete, invoke CB
2138             const CdbExtensionCommandPtr command = m_extensionCommandQueue.takeAt(index);
2139             if (t == 'R') {
2140                 command->success = true;
2141                 command->reply = message;
2142             } else {
2143                 command->success = false;
2144                 command->errorMessage = message;
2145             }
2146             if (debug)
2147                 qDebug("### Completed extension command '%s', token=%d, pending=%d",
2148                        command->command.constData(), command->token, m_extensionCommandQueue.size());
2149             if (command->handler)
2150                 (this->*(command->handler))(command);
2151             return;
2152         }
2153     }
2154
2155     if (what == "debuggee_output") {
2156         showMessage(StringFromBase64EncodedUtf16(message), AppOutput);
2157         return;
2158     }
2159
2160     if (what == "event") {
2161         showStatusMessage(QString::fromAscii(message),  5000);
2162         return;
2163     }
2164
2165     if (what == "session_accessible") {
2166         if (!m_accessible) {
2167             m_accessible = true;
2168             handleSessionAccessible(message.toULong());
2169         }
2170         return;
2171     }
2172
2173     if (what == "session_inaccessible") {
2174         if (m_accessible) {
2175             m_accessible = false;
2176             handleSessionInaccessible(message.toULong());
2177         }
2178         return;
2179     }
2180
2181     if (what == "session_idle") {
2182         handleSessionIdle(message);
2183         return;
2184     }
2185
2186     if (what == "exception") {
2187         WinException exception;
2188         GdbMi gdbmi;
2189         gdbmi.fromString(message);
2190         exception.fromGdbMI(gdbmi);
2191         const QString message = exception.toString(true);
2192         showStatusMessage(message);
2193 #ifdef Q_OS_WIN // Report C++ exception in application output as well.
2194         if (exception.exceptionCode == winExceptionCppException)
2195             showMessage(message + QLatin1Char('\n'), AppOutput);
2196 #endif
2197         return;
2198     }
2199
2200     return;
2201 }
2202
2203 // Check for a CDB prompt '0:000> ' ('process:thread> ')..no regexps for QByteArray...
2204 enum { CdbPromptLength = 7 };
2205
2206 static inline bool isCdbPrompt(const QByteArray &c)
2207 {
2208     return c.size() >= CdbPromptLength && c.at(6) == ' ' && c.at(5) == '>' && c.at(1) == ':'
2209             && std::isdigit(c.at(0)) &&  std::isdigit(c.at(2)) && std::isdigit(c.at(3))
2210             && std::isdigit(c.at(4));
2211 }
2212
2213 // Check for '<token>32>' or '<token>32<'
2214 static inline bool checkCommandToken(const QByteArray &tokenPrefix, const QByteArray &c,
2215                                   int *token, bool *isStart)
2216 {
2217     *token = 0;
2218     *isStart = false;
2219     const int tokenPrefixSize = tokenPrefix.size();
2220     const int size = c.size();
2221     if (size < tokenPrefixSize + 2 || !std::isdigit(c.at(tokenPrefixSize)))
2222         return false;
2223     switch (c.at(size - 1)) {
2224     case '>':
2225         *isStart = false;
2226         break;
2227     case '<':
2228         *isStart = true;
2229         break;
2230     default:
2231         return false;
2232     }
2233     if (!c.startsWith(tokenPrefix))
2234         return false;
2235     bool ok;
2236     *token = c.mid(tokenPrefixSize, size - tokenPrefixSize - 1).toInt(&ok);
2237     return ok;
2238 }
2239
2240 void CdbEngine::parseOutputLine(QByteArray line)
2241 {
2242     // The hooked output callback in the extension suppresses prompts,
2243     // it should happen only in initial and exit stages. Note however that
2244     // if the output is not hooked, sequences of prompts are possible which
2245     // can mix things up.
2246     while (isCdbPrompt(line))
2247         line.remove(0, CdbPromptLength);
2248     // An extension notification (potentially consisting of several chunks)
2249     if (line.startsWith(m_creatorExtPrefix)) {
2250         // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
2251         const char type = line.at(m_creatorExtPrefix.size());
2252         // integer token
2253         const int tokenPos = m_creatorExtPrefix.size() + 2;
2254         const int tokenEndPos = line.indexOf('|', tokenPos);
2255         QTC_ASSERT(tokenEndPos != -1, return)
2256         const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
2257         // remainingChunks
2258         const int remainingChunksPos = tokenEndPos + 1;
2259         const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
2260         QTC_ASSERT(remainingChunksEndPos != -1, return)
2261         const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
2262         // const char 'serviceName'
2263         const int whatPos = remainingChunksEndPos + 1;
2264         const int whatEndPos = line.indexOf('|', whatPos);
2265         QTC_ASSERT(whatEndPos != -1, return)
2266         const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
2267         // Build up buffer, call handler once last chunk was encountered
2268         m_extensionMessageBuffer += line.mid(whatEndPos + 1);
2269         if (remainingChunks == 0) {
2270             handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
2271             m_extensionMessageBuffer.clear();
2272         }
2273         return;
2274     }
2275     // Check for command start/end tokens within which the builtin command
2276     // output is enclosed
2277     int token = 0;
2278     bool isStartToken = false;
2279     const bool isCommandToken = checkCommandToken(m_tokenPrefix, line, &token, &isStartToken);
2280     if (debug > 1)
2281         qDebug("Reading CDB stdout '%s',\n  isCommand=%d, token=%d, isStart=%d, current=%d",
2282                line.constData(), isCommandToken, token, isStartToken, m_currentBuiltinCommandIndex);
2283
2284     // If there is a current command, wait for end of output indicated by token,
2285     // command, trigger handler and finish, else append to its output.
2286     if (m_currentBuiltinCommandIndex != -1) {
2287         QTC_ASSERT(!isStartToken && m_currentBuiltinCommandIndex < m_builtinCommandQueue.size(), return; );
2288         const CdbBuiltinCommandPtr &currentCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2289         if (isCommandToken) {
2290             // Did the command finish? Invoke callback and remove from queue.
2291             if (debug)
2292                 qDebug("### Completed builtin command '%s', token=%d, %d lines, pending=%d",
2293                        currentCommand->command.constData(), currentCommand->token,
2294                        currentCommand->reply.size(), m_builtinCommandQueue.size() - 1);
2295             QTC_ASSERT(token == currentCommand->token, return; );
2296             if (currentCommand->handler)
2297                 (this->*(currentCommand->handler))(currentCommand);
2298             m_builtinCommandQueue.removeAt(m_currentBuiltinCommandIndex);
2299             m_currentBuiltinCommandIndex = -1;
2300         } else {
2301             // Record output of current command
2302             currentCommand->reply.push_back(line);
2303         }
2304         return;
2305     } // m_currentCommandIndex
2306     if (isCommandToken) {
2307         // Beginning command token encountered, start to record output.
2308         const int index = indexOfCommand(m_builtinCommandQueue, token);
2309         QTC_ASSERT(isStartToken && index != -1, return; );
2310         m_currentBuiltinCommandIndex = index;
2311         const CdbBuiltinCommandPtr &currentCommand = m_builtinCommandQueue.at(m_currentBuiltinCommandIndex);
2312         if (debug)
2313             qDebug("### Gathering output for '%s' token %d", currentCommand->command.constData(), currentCommand->token);
2314         return;
2315     }
2316
2317     showMessage(QString::fromLocal8Bit(line), LogMisc);
2318 }
2319
2320 void CdbEngine::readyReadStandardOut()
2321 {
2322     if (m_ignoreCdbOutput)
2323         return;
2324     m_outputBuffer += m_process.readAllStandardOutput();
2325     // Split into lines and parse line by line.
2326     while (true) {
2327         const int endOfLinePos = m_outputBuffer.indexOf('\n');
2328         if (endOfLinePos == -1) {
2329             break;
2330         } else {
2331             // Check for '\r\n'
2332             QByteArray line = m_outputBuffer.left(endOfLinePos);
2333             if (!line.isEmpty() && line.at(line.size() - 1) == '\r')
2334                 line.truncate(line.size() - 1);
2335             parseOutputLine(line);
2336             m_outputBuffer.remove(0, endOfLinePos + 1);
2337         }
2338     }
2339 }
2340
2341 void CdbEngine::readyReadStandardError()
2342 {
2343     showMessage(QString::fromLocal8Bit(m_process.readAllStandardError()), LogError);
2344 }
2345
2346 void CdbEngine::processError()
2347 {
2348     showMessage(m_process.errorString(), LogError);
2349 }
2350
2351 #if 0
2352 // Join breakpoint ids for a multi-breakpoint id commands like 'bc', 'be', 'bd'
2353 static QByteArray multiBreakpointCommand(const char *cmdC, const Breakpoints &bps)
2354 {
2355     QByteArray cmd(cmdC);
2356     ByteArrayInputStream str(cmd);
2357     foreach(const BreakpointData *bp, bps)
2358         str << ' ' << bp->bpNumber;
2359     return cmd;
2360 }
2361 #endif
2362
2363 bool CdbEngine::stateAcceptsBreakpointChanges() const
2364 {
2365     switch (state()) {
2366     case InferiorRunOk:
2367     case InferiorStopOk:
2368     return true;
2369     default:
2370         break;
2371     }
2372     return false;
2373 }
2374
2375 bool CdbEngine::acceptsBreakpoint(BreakpointId id) const
2376 {
2377     const BreakpointParameters &data = breakHandler()->breakpointData(id);
2378     if (!DebuggerEngine::isCppBreakpoint(data))
2379         return false;
2380     switch (data.type) {
2381     case UnknownType:
2382     case BreakpointAtFork:
2383     case WatchpointAtExpression:
2384     case BreakpointAtSysCall:
2385         return false;
2386     case WatchpointAtAddress:
2387     case BreakpointByFileAndLine:
2388     case BreakpointByFunction:
2389     case BreakpointByAddress:
2390     case BreakpointAtThrow:
2391     case BreakpointAtCatch:
2392     case BreakpointAtMain:
2393     case BreakpointAtExec:
2394         break;
2395     }
2396     return true;
2397 }
2398
2399 // Context for fixing file/line-type breakpoints, for delayed creation.
2400 class BreakpointCorrectionContext
2401 {
2402 public:
2403     explicit BreakpointCorrectionContext(const CPlusPlus::Snapshot &s,
2404                                          const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy) :
2405         m_snapshot(s), m_workingCopy(workingCopy) {}
2406
2407     unsigned fixLineNumber(const QString &fileName, unsigned lineNumber) const;
2408
2409 private:
2410     const CPlusPlus::Snapshot m_snapshot;
2411     CPlusPlus::CppModelManagerInterface::WorkingCopy m_workingCopy;
2412 };
2413
2414 static CPlusPlus::Document::Ptr getParsedDocument(const QString &fileName,
2415                                                   const CPlusPlus::CppModelManagerInterface::WorkingCopy &workingCopy,
2416                                                   const CPlusPlus::Snapshot &snapshot)
2417 {
2418     QString src;
2419     if (workingCopy.contains(fileName)) {
2420         src = workingCopy.source(fileName);
2421     } else {
2422         Utils::FileReader reader;
2423         if (reader.fetch(fileName)) // ### FIXME error reporting
2424             src = QString::fromLocal8Bit(reader.data()); // ### FIXME encoding
2425     }
2426
2427     QByteArray source = snapshot.preprocessedCode(src, fileName);
2428
2429     CPlusPlus::Document::Ptr doc = snapshot.documentFromSource(source, fileName);
2430     doc->parse();
2431     return doc;
2432 }
2433
2434 unsigned BreakpointCorrectionContext::fixLineNumber(const QString &fileName,
2435                                                     unsigned lineNumber) const
2436 {
2437     CPlusPlus::Document::Ptr doc = m_snapshot.document(fileName);
2438     if (!doc || !doc->translationUnit()->ast())
2439         doc = getParsedDocument(fileName, m_workingCopy, m_snapshot);
2440
2441     CPlusPlus::FindCdbBreakpoint findVisitor(doc->translationUnit());
2442     const unsigned correctedLine = findVisitor(lineNumber);
2443     if (!correctedLine) {
2444         qWarning("Unable to find breakpoint location for %s:%d",
2445                  qPrintable(QDir::toNativeSeparators(fileName)), lineNumber);
2446         return lineNumber;
2447     }
2448     if (debug)
2449         qDebug("Code model: Breakpoint line %u -> %u in %s",
2450                lineNumber, correctedLine, qPrintable(fileName));
2451     return correctedLine;
2452 }
2453
2454 void CdbEngine::attemptBreakpointSynchronization()
2455 {
2456
2457
2458     if (debug)
2459         qDebug("attemptBreakpointSynchronization in %s", stateName(state()));
2460     // Check if there is anything to be done at all.
2461     BreakHandler *handler = breakHandler();
2462     // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only?
2463     foreach (BreakpointId id, handler->unclaimedBreakpointIds())
2464         if (acceptsBreakpoint(id))
2465             handler->setEngine(id, this);
2466
2467     // Quick check: is there a need to change something? - Populate module cache
2468     bool changed = false;
2469     const BreakpointIds ids = handler->engineBreakpointIds(this);
2470     foreach (BreakpointId id, ids) {
2471         switch (handler->state(id)) {
2472         case BreakpointInsertRequested:
2473         case BreakpointRemoveRequested:
2474         case BreakpointChangeRequested:
2475             changed = true;
2476             break;
2477         case BreakpointInserted: {
2478             // Collect the new modules matching the files.
2479             // In the future, that information should be obtained from the build system.
2480             const BreakpointParameters &data = handler->breakpointData(id);
2481             if (data.type == BreakpointByFileAndLine && !data.module.isEmpty())
2482                 m_fileNameModuleHash.insert(data.fileName, data.module);
2483         }
2484         break;
2485         default:
2486             break;
2487         }
2488     }
2489
2490     if (debugBreakpoints)
2491         qDebug("attemptBreakpointSynchronizationI %dms accessible=%d, %s %d breakpoints, changed=%d",
2492                elapsedLogTime(), m_accessible, stateName(state()), ids.size(), changed);
2493     if (!changed)
2494         return;
2495
2496     if (!m_accessible) {
2497         // No nested calls.
2498         if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
2499             doInterruptInferior(SpecialStopSynchronizeBreakpoints);
2500         return;
2501     }
2502     // Add/Change breakpoints and store pending ones in map, since
2503     // Breakhandler::setResponse() on pending breakpoints clears the pending flag.
2504     // handleBreakPoints will the complete that information and set it on the break handler.
2505     bool addedChanged = false;
2506     QScopedPointer<BreakpointCorrectionContext> lineCorrection;
2507     foreach (BreakpointId id, ids) {
2508         BreakpointParameters parameters = handler->breakpointData(id);
2509         BreakpointResponse response;
2510         response.fromParameters(parameters);
2511         // If we encountered that file and have a module for it: Add it.
2512         if (parameters.type == BreakpointByFileAndLine && parameters.module.isEmpty()) {
2513             const QHash<QString, QString>::const_iterator it = m_fileNameModuleHash.constFind(parameters.fileName);
2514             if (it != m_fileNameModuleHash.constEnd())
2515                 parameters.module = it.value();
2516         }
2517         switch (handler->state(id)) {
2518         case BreakpointInsertRequested:
2519             if (parameters.type == BreakpointByFileAndLine) {
2520                 if (lineCorrection.isNull())
2521                     lineCorrection.reset(new BreakpointCorrectionContext(debuggerCore()->cppCodeModelSnapshot(),
2522                                                                          CPlusPlus::CppModelManagerInterface::instance()->workingCopy()));
2523                 response.lineNumber = lineCorrection->fixLineNumber(parameters.fileName, parameters.lineNumber);
2524                 postCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false), 0);
2525             } else {
2526                 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2527             }
2528             if (!parameters.enabled)
2529                 postCommand("bd " + QByteArray::number(id), 0);
2530             handler->notifyBreakpointInsertProceeding(id);
2531             handler->notifyBreakpointInsertOk(id);
2532             m_pendingBreakpointMap.insert(id, response);
2533             addedChanged = true;
2534             // Ensure enabled/disabled is correct in handler and line number is there.
2535             handler->setResponse(id, response);
2536             if (debugBreakpoints)
2537                 qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
2538             break;
2539         case BreakpointChangeRequested:
2540             handler->notifyBreakpointChangeProceeding(id);
2541             if (debugBreakpoints)
2542                 qDebug("Changing %llu:\n    %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
2543                        qPrintable(parameters.toString()));
2544             if (parameters.enabled != handler->response(id).enabled) {
2545                 // Change enabled/disabled breakpoints without triggering update.
2546                 postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
2547                 response.pending = false;
2548                 response.enabled = parameters.enabled;
2549                 handler->setResponse(id, response);
2550             } else {
2551                 // Delete and re-add, triggering update
2552                 addedChanged = true;
2553                 postCommand("bc " + QByteArray::number(id), 0);
2554                 postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
2555                 m_pendingBreakpointMap.insert(id, response);
2556             }
2557             handler->notifyBreakpointChangeOk(id);
2558             break;
2559         case BreakpointRemoveRequested:
2560             postCommand("bc " + QByteArray::number(id), 0);
2561             handler->notifyBreakpointRemoveProceeding(id);
2562             handler->notifyBreakpointRemoveOk(id);
2563             m_pendingBreakpointMap.remove(id);
2564             break;
2565         default:
2566             break;
2567         }
2568     }
2569     // List breakpoints and send responses
2570     if (addedChanged)
2571         postCommandSequence(CommandListBreakPoints);
2572 }
2573
2574 // Pass a file name through source mapping and normalize upper/lower case (for the editor
2575 // manager to correctly process it) and convert to clean path.
2576 CdbEngine::NormalizedSourceFileName CdbEngine::sourceMapNormalizeFileNameFromDebugger(const QString &f)
2577 {
2578     // 1) Check cache.
2579     QMap<QString, NormalizedSourceFileName>::const_iterator it = m_normalizedFileCache.constFind(f);
2580     if (it != m_normalizedFileCache.constEnd())
2581         return it.value();
2582     if (debugSourceMapping)
2583         qDebug(">sourceMapNormalizeFileNameFromDebugger %s", qPrintable(f));
2584     // Do we have source path mappings? ->Apply.
2585     const QString fileName = cdbSourcePathMapping(QDir::toNativeSeparators(f), m_sourcePathMappings,
2586                                                   DebuggerToSource);
2587     // Up/lower case normalization according to Windows.
2588 #ifdef Q_OS_WIN
2589     QString normalized = Utils::normalizePathName(fileName);
2590 #else
2591     QString normalized = fileName;
2592 #endif
2593     if (debugSourceMapping)
2594         qDebug(" sourceMapNormalizeFileNameFromDebugger %s->%s", qPrintable(fileName), qPrintable(normalized));
2595     // Check if it really exists, that is normalize worked and QFileInfo confirms it.
2596     const bool exists = !normalized.isEmpty() && QFileInfo(normalized).isFile();
2597     NormalizedSourceFileName result(QDir::cleanPath(normalized.isEmpty() ? fileName : normalized), exists);
2598     if (!exists) {
2599         // At least upper case drive letter if failed.
2600         if (result.fileName.size() > 2 && result.fileName.at(1) == QLatin1Char(':'))
2601             result.fileName[0] = result.fileName.at(0).toUpper();
2602     }
2603     m_normalizedFileCache.insert(f, result);
2604     if (debugSourceMapping)
2605         qDebug("<sourceMapNormalizeFileNameFromDebugger %s %d", qPrintable(result.fileName), result.exists);
2606     return result;
2607 }
2608
2609 // Parse frame from GDBMI. Duplicate of the gdb code, but that
2610 // has more processing.
2611 static StackFrames parseFrames(const GdbMi &gdbmi)
2612 {
2613     StackFrames rc;
2614     const int count = gdbmi.childCount();
2615     rc.reserve(count);
2616     for (int i = 0; i  < count; i++) {
2617         const GdbMi &frameMi = gdbmi.childAt(i);
2618         StackFrame frame;
2619         frame.level = i;
2620         const GdbMi fullName = frameMi.findChild("fullname");
2621         if (fullName.isValid()) {
2622             frame.file = QFile::decodeName(fullName.data());
2623             frame.line = frameMi.findChild("line").data().toInt();
2624             frame.usable = false; // To be decided after source path mapping.
2625         }
2626         frame.function = QLatin1String(frameMi.findChild("func").data());
2627         frame.from = QLatin1String(frameMi.findChild("from").data());
2628         frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
2629         rc.push_back(frame);
2630     }
2631     return rc;
2632 }
2633
2634 unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
2635 {
2636     // Parse frames, find current. Special handling for step into:
2637     // When stepping into on an actual function (source mode) by executing 't', an assembler
2638     // frame pointing at the jmp instruction is hit (noticeable by top function being
2639     // 'ILT+'). If that is the case, execute another 't' to step into the actual function.    .
2640     // Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
2641     int current = -1;
2642     StackFrames frames = parseFrames(data);
2643     const int count = frames.size();
2644     for (int i = 0; i < count; i++) {
2645         const bool hasFile = !frames.at(i).file.isEmpty();
2646         // jmp-frame hit by step into, do another 't' and abort sequence.
2647         if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
2648             showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
2649             return ParseStackStepInto;
2650         }
2651         if (hasFile) {
2652             const NormalizedSourceFileName fileName = sourceMapNormalizeFileNameFromDebugger(frames.at(i).file);
2653             frames[i].file = fileName.fileName;
2654             frames[i].usable = fileName.exists;
2655             if (current == -1 && frames[i].usable)
2656                 current = i;
2657         }
2658     }
2659     if (count && current == -1) // No usable frame, use assembly.
2660         current = 0;
2661     // Set
2662     stackHandler()->setFrames(frames);
2663     activateFrame(current);
2664     return 0;
2665 }
2666
2667 void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
2668 {
2669     if (command->success) {
2670         GdbMi data;
2671         data.fromString(command->reply);
2672         parseStackTrace(data, false);
2673         postCommandSequence(command->commandSequence);
2674     } else {
2675         showMessage(command->errorMessage, LogError);
2676     }
2677 }
2678
2679 void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
2680 {
2681     int value = 0;
2682     if (command->success) {
2683         value = command->reply.toInt();
2684     } else {
2685         showMessage(command->errorMessage, LogError);
2686     }
2687     // Is this a conditional breakpoint?
2688     if (command->cookie.isValid() && qVariantCanConvert<ConditionalBreakPointCookie>(command->cookie)) {
2689         const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
2690         const QString message = value ?
2691             tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
2692             arg(value).arg(cookie.id) :
2693             tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
2694             arg(cookie.id);
2695         showMessage(message, LogMisc);
2696         // Stop if evaluation is true, else continue
2697         if (value) {
2698             processStop(cookie.stopReason, true);
2699         } else {
2700             postCommand("g", 0);
2701         }
2702     }
2703 }
2704
2705 void CdbEngine::evaluateExpression(QByteArray exp, const QVariant &cookie)
2706 {
2707     if (exp.contains(' ') && !exp.startsWith('"')) {
2708         exp.prepend('"');
2709         exp.append('"');
2710     }
2711     postExtensionCommand("expression", exp, 0, &CdbEngine::handleExpression, 0, cookie);
2712 }
2713
2714 void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
2715 {
2716     postCommandSequence(command->commandSequence);
2717 }
2718
2719 // Post a sequence of standard commands: Trigger next once one completes successfully
2720 void CdbEngine::postCommandSequence(unsigned mask)
2721 {
2722     if (debug)
2723         qDebug("postCommandSequence 0x%x\n", mask);
2724
2725     if (!mask)
2726         return;
2727     if (mask & CommandListThreads) {
2728         postExtensionCommand("threads", QByteArray(), 0, &CdbEngine::handleThreads, mask & ~CommandListThreads);
2729         return;
2730     }
2731     if (mask & CommandListStack) {
2732         postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
2733         return;
2734     }
2735     if (mask & CommandListRegisters) {
2736         QTC_ASSERT(threadsHandler()->currentThread() >= 0,  return; )
2737         postExtensionCommand("registers", QByteArray(), 0, &CdbEngine::handleRegisters, mask & ~CommandListRegisters);
2738         return;
2739     }
2740     if (mask & CommandListModules) {
2741         postExtensionCommand("modules", QByteArray(), 0, &CdbEngine::handleModules, mask & ~CommandListModules);
2742         return;
2743     }
2744     if (mask & CommandListBreakPoints) {
2745         postExtensionCommand("breakpoints", QByteArray("-v"), 0,
2746                              &CdbEngine::handleBreakPoints, mask & ~CommandListBreakPoints);
2747         return;
2748     }
2749 }
2750
2751 void CdbEngine::handleWidgetAt(const CdbExtensionCommandPtr &reply)
2752 {
2753     bool success = false;
2754     QString message;
2755     do {
2756         if (!reply->success) {
2757             message = QString::fromAscii(reply->errorMessage);
2758             break;
2759         }
2760         // Should be "namespace::QWidget:0x555"
2761         QString watchExp = QString::fromAscii(reply->reply);
2762         const int sepPos = watchExp.lastIndexOf(QLatin1Char(':'));
2763         if (sepPos == -1) {
2764             message = QString::fromAscii("Invalid output: %1").arg(watchExp);
2765             break;
2766         }
2767         // 0x000 -> nothing found
2768         if (!watchExp.mid(sepPos + 1).toULongLong(0, 0)) {
2769             message = QString::fromAscii("No widget could be found at %1, %2.").arg(m_watchPointX).arg(m_watchPointY);
2770             break;
2771         }
2772         // Turn into watch expression: "*(namespace::QWidget*)0x555"
2773         watchExp.replace(sepPos, 1, QLatin1String("*)"));
2774         watchExp.insert(0, QLatin1String("*("));
2775         watchHandler()->watchExpression(watchExp);
2776         success = true;
2777     } while (false);
2778     if (!success)
2779         showMessage(message, LogWarning);
2780     m_watchPointX = m_watchPointY = 0;
2781 }
2782
2783 static inline void formatCdbBreakPointResponse(BreakpointId id, const BreakpointResponse &r,
2784                                                   QTextStream &str)
2785 {
2786     str << "Obtained breakpoint " << id << " (#" << r.number << ')';
2787     if (r.pending) {
2788         str << ", pending";
2789     } else {
2790         str.setIntegerBase(16);
2791         str << ", at 0x" << r.address;
2792         str.setIntegerBase(10);
2793     }
2794     if (!r.enabled)
2795         str << ", disabled";
2796     if (!r.module.isEmpty())
2797         str << ", module: '" << r.module << '\'';
2798     str << '\n';
2799 }
2800
2801 void CdbEngine::handleBreakPoints(const CdbExtensionCommandPtr &reply)
2802 {
2803     if (debugBreakpoints)
2804         qDebug("CdbEngine::handleBreakPoints: success=%d: %s", reply->success, reply->reply.constData());
2805     if (!reply->success) {
2806         showMessage(QString::fromAscii(reply->errorMessage), LogError);
2807         return;
2808     }
2809     GdbMi value;
2810     value.fromString(reply->reply);
2811     if (value.type() != GdbMi::List) {
2812         showMessage(QString::fromAscii("Unabled to parse breakpoints reply"), LogError);
2813         return;
2814     }
2815     handleBreakPoints(value);
2816 }
2817
2818 void CdbEngine::handleBreakPoints(const GdbMi &value)
2819 {
2820     // Report all obtained parameters back. Note that not all parameters are reported
2821     // back, so, match by id and complete
2822     if (debugBreakpoints)
2823         qDebug("\nCdbEngine::handleBreakPoints with %d", value.childCount());
2824     QString message;
2825     QTextStream str(&message);
2826     BreakHandler *handler = breakHandler();
2827     foreach (const GdbMi &breakPointG, value.children()) {
2828         BreakpointResponse reportedResponse;
2829         const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
2830         if (debugBreakpoints)
2831             qDebug("  Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
2832                    qPrintable(reportedResponse.toString()));
2833
2834         if (!reportedResponse.pending) {
2835             const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
2836             if (it != m_pendingBreakpointMap.end()) {
2837                 // Complete the response and set on handler.
2838                 BreakpointResponse &currentResponse = it.value();
2839                 currentResponse.number = reportedResponse.number;
2840                 currentResponse.address = reportedResponse.address;
2841                 currentResponse.module = reportedResponse.module;
2842                 currentResponse.pending = reportedResponse.pending;
2843                 currentResponse.enabled = reportedResponse.enabled;
2844                 formatCdbBreakPointResponse(id, currentResponse, str);
2845                 if (debugBreakpoints)
2846                     qDebug("  Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
2847                 handler->setResponse(id, currentResponse);
2848                 m_pendingBreakpointMap.erase(it);
2849             }
2850         } // not pending reported
2851     } // foreach
2852     if (m_pendingBreakpointMap.empty()) {
2853         str << QLatin1String("All breakpoints have been resolved.\n");
2854     } else {
2855         str << QString::fromLatin1("%1 breakpoint(s) pending...\n").arg(m_pendingBreakpointMap.size());
2856     }
2857     showMessage(message, LogMisc);
2858 }
2859
2860 void CdbEngine::watchPoint(const QPoint &p)
2861 {
2862     m_watchPointX = p.x();
2863     m_watchPointY = p.y();
2864     switch (state()) {
2865     case InferiorStopOk:
2866         postWidgetAtCommand();
2867         break;
2868     case InferiorRunOk:
2869         // "Select Widget to Watch" from a running application is currently not
2870         // supported. It could be implemented via SpecialStopGetWidgetAt-mode,
2871         // but requires some work as not to confuse the engine by state-change notifications
2872         // emitted by the debuggee function call.
2873         showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
2874         break;
2875     default:
2876         showMessage(tr("\"Select Widget to Watch\": Not supported in state '%1'.").
2877                     arg(QString::fromAscii(stateName(state()))), LogWarning);
2878         break;
2879     }
2880 }
2881
2882 void CdbEngine::postWidgetAtCommand()
2883 {
2884     QByteArray arguments = QByteArray::number(m_watchPointX);
2885     arguments.append(' ');
2886     arguments.append(QByteArray::number(m_watchPointY));
2887     postExtensionCommand("widgetat", arguments, 0, &CdbEngine::handleWidgetAt, 0);
2888 }
2889
2890 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
2891 {
2892     if (qVariantCanConvert<MemoryChangeCookie>(v)) {
2893         const MemoryChangeCookie changeData = qVariantValue<MemoryChangeCookie>(v);
2894         postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data), 0);
2895         return;
2896     }
2897     if (qVariantCanConvert<MemoryViewCookie>(v)) {
2898         postFetchMemory(qVariantValue<MemoryViewCookie>(v));
2899         return;
2900     }
2901 }
2902
2903 } // namespace Internal
2904 } // namespace Debugger