cdbengine.cpp 71.5 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
7
8
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
con's avatar
con committed
9
** No Commercial Usage
10
**
con's avatar
con committed
11
12
13
14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
15
16
17
18
19
20
21
22
23
24
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
con's avatar
con committed
25
26
27
28
29
30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
31
32
33
**
**************************************************************************/

34
#include "cdbengine.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
35
36
#include "debuggerstartparameters.h"
#include "disassemblerlines.h"
37
38
#include "cdboptions.h"
#include "cdboptionspage.h"
39
40
41
42
43
44
45
#include "bytearrayinputstream.h"
#include "breakpoint.h"
#include "breakhandler.h"
#include "stackframe.h"
#include "stackhandler.h"
#include "watchhandler.h"
#include "threadshandler.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
46
#include "moduleshandler.h"
47
48
49
#include "debuggeractions.h"
#include "debuggercore.h"
#include "registerhandler.h"
50
51
#include "disassembleragent.h"
#include "memoryagent.h"
52
#include "debuggerrunner.h"
53
#include "debuggertooltip.h"
54
55
56
#include "cdbparsehelpers.h"
#include "watchutils.h"
#include "gdb/gdbmi.h"
57
58
59
#include "shared/cdbsymbolpathlisteditor.h"

#include <coreplugin/icore.h>
60
#include <texteditor/itexteditor.h>
61
#include <projectexplorer/toolchain.h>
62

63
#include <utils/synchronousprocess.h>
64
65
66
67
68
69
70
71
72
73
#include <utils/winutils.h>
#include <utils/qtcassert.h>
#include <utils/savedaction.h>

#include <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
#include <QtCore/QDateTime>
74
#include <QtGui/QToolTip>
75
76
77
78
79
80
81
82

#ifdef Q_OS_WIN
#    include <utils/winutils.h>
#    include "dbgwinutils.h"
#endif

#include <cctype>

83
84
Q_DECLARE_METATYPE(Debugger::Internal::DisassemblerAgent*)
Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
85
86

enum { debug = 0 };
87
enum { debugLocals = 0 };
88
89
90
91
92
93
94
95
96
97
enum { debugBreakpoints = 0 };

#if 0
#  define STATE_DEBUG(func, line, notifyFunc) qDebug("%s at %s:%d", notifyFunc, func, line);
#else
#  define STATE_DEBUG(func, line, notifyFunc)
#endif

/* CdbEngine version 2: Run the CDB process on pipes and parse its output.
 * The engine relies on a CDB extension Qt Creator provides as an extension
98
 * library (32/64bit), which is loaded into cdb.exe. It serves to:
99
 * - Notify the engine about the state of the debugging session:
100
 *   + idle: (hooked up with .idle_cmd) debuggee stopped
101
102
103
 *   + accessible: Debuggee stopped, cdb.exe accepts commands
 *   + inaccessible: Debuggee runs, no way to post commands
 *   + session active/inactive: Lost debuggee, terminating.
104
105
 * - Hook up with output/event callbacks and produce formatted output to be able
 *   to catch application output and exceptions.
106
107
108
109
110
111
 * - Provide some extension commands that produce output in a standardized (GDBMI)
 *   format that ends up in handleExtensionMessage().
 *   + pid     Return debuggee pid for interrupting.
 *   + locals  Print locals from SymbolGroup
 *   + expandLocals Expand locals in symbol group
 *   + registers, modules, threads
112
113
114
115
116
117
118
119
 * Commands can be posted by calling:
 * 1) postCommand(): Does not expect a reply
 * 2) postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
 *    that is captured by enclosing it in special tokens using the 'echo' command and
 *    then invokes a callback with a CdbBuiltinCommand structure.
 * 3) postExtensionCommand(): Run a command provided by the extension producing
 *    one-line output and invoke a callback with a CdbExtensionCommand structure
 *    (output is potentially split up in chunks). */
120

121
122
using namespace ProjectExplorer;

123
namespace Debugger {
124
namespace Internal {
125

126
127
static const char localsPrefixC[] = "local.";

128
struct MemoryViewCookie {
129
    explicit MemoryViewCookie(MemoryAgent *a = 0, QObject *e = 0,
130
131
132
                              quint64 addr = 0, quint64 l = 0) :
        agent(a), editorToken(e), address(addr), length(l) {}

133
    MemoryAgent *agent;
134
135
136
137
138
139
140
141
142
143
144
145
146
    QObject *editorToken;
    quint64 address;
    quint64 length;
};

struct SourceLocationCookie {
    explicit SourceLocationCookie(const QString &f = QString(), int l = 0) :
             fileName(f), lineNumber(l) {}

    QString fileName;
    int lineNumber;
};

147
} // namespace Internal
148
149
} // namespace Debugger

150
151
Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
Q_DECLARE_METATYPE(Debugger::Internal::SourceLocationCookie)
152
153

namespace Debugger {
154
namespace Internal {
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257

// Base data structure for command queue entries with callback
struct CdbCommandBase
{
    typedef CdbEngine::BuiltinCommandHandler CommandHandler;

    CdbCommandBase();
    CdbCommandBase(const QByteArray  &cmd, int token, unsigned flags,
                   unsigned nc, const QVariant &cookie);

    int token;
    unsigned flags;
    QByteArray command;
    QVariant cookie;
    // Continue with another commands as specified in CommandSequenceFlags
    unsigned commandSequence;
};

CdbCommandBase::CdbCommandBase() :
    token(0), flags(0), commandSequence(0)
{
}

CdbCommandBase::CdbCommandBase(const QByteArray  &cmd, int t, unsigned f,
                               unsigned nc, const QVariant &c) :
    token(t), flags(f), command(cmd), cookie(c), commandSequence(nc)
{
}

// Queue entry for builtin commands producing free-format
// line-by-line output.
struct CdbBuiltinCommand : public CdbCommandBase
{
    typedef CdbEngine::BuiltinCommandHandler CommandHandler;

    CdbBuiltinCommand() {}
    CdbBuiltinCommand(const QByteArray  &cmd, int token, unsigned flags,
                      CommandHandler h,
                      unsigned nc, const QVariant &cookie) :
        CdbCommandBase(cmd, token, flags, nc, cookie), handler(h) {}


    QByteArray joinedReply() const;

    CommandHandler handler;
    QList<QByteArray> reply;
};

QByteArray CdbBuiltinCommand::joinedReply() const
{
    if (reply.isEmpty())
        return QByteArray();
    QByteArray answer;
    answer.reserve(120  * reply.size());
    foreach (const QByteArray &l, reply) {
        answer += l;
        answer += '\n';
    }
    return answer;
}

// Queue entry for Qt Creator extension commands producing one-line
// output with success flag and error message.
struct CdbExtensionCommand : public CdbCommandBase
{
    typedef CdbEngine::ExtensionCommandHandler CommandHandler;

    CdbExtensionCommand() : success(false) {}
    CdbExtensionCommand(const QByteArray  &cmd, int token, unsigned flags,
                      CommandHandler h,
                      unsigned nc, const QVariant &cookie) :
        CdbCommandBase(cmd, token, flags, nc, cookie), handler(h),success(false) {}

    CommandHandler handler;
    QByteArray reply;
    QByteArray errorMessage;
    bool success;
};

template <class CommandPtrType>
int indexOfCommand(const QList<CommandPtrType> &l, int token)
{
    const int count = l.size();
    for (int i = 0; i < count; i++)
        if (l.at(i)->token == token)
            return i;
    return -1;
}

static inline bool validMode(DebuggerStartMode sm)
{
    switch (sm) {
    case NoStartMode:
    case AttachTcf:
    case AttachCore:
    case StartRemoteGdb:
        return false;
    default:
        break;
    }
    return true;
}

258
static inline QString msgCdbDisabled(ToolChainType tc)
259
260
{
    return CdbEngine::tr("The CDB debug engine required for %1 is currently disabled.").
261
                      arg(ToolChain::toolChainName(tc));
262
263
}

264
// Accessed by RunControlFactory
265
266
DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp,
    DebuggerEngine *masterEngine, QString *errorMessage)
267
268
269
{
#ifdef Q_OS_WIN
    CdbOptionsPage *op = CdbOptionsPage::instance();
270
    if (!op || !op->options()->isValid()) {
271
        *errorMessage = msgCdbDisabled(sp.toolChainType);
272
273
274
275
276
277
        return 0;
    }
    if (!validMode(sp.startMode)) {
        *errorMessage = CdbEngine::tr("The CDB debug engine does not support start mode %1.").arg(sp.startMode);
        return 0;
    }
278
    return new CdbEngine(sp, masterEngine, op->options());
279
#else
280
    Q_UNUSED(masterEngine)
281
282
    Q_UNUSED(sp)
#endif
283
    *errorMessage = QString::fromLatin1("Unsupported debug mode");
284
285
286
287
288
289
    return 0;
}

bool isCdbEngineEnabled()
{
#ifdef Q_OS_WIN
290
    return CdbOptionsPage::instance() && CdbOptionsPage::instance()->options()->isValid();
291
292
293
294
295
#else
    return false;
#endif
}

296
ConfigurationCheck checkCdbConfiguration(ToolChainType toolChain)
297
{
298
    ConfigurationCheck check;
299
    switch (toolChain) {
300
301
302
303
304
305
    case ToolChain_MinGW: // Do our best
    case ToolChain_MSVC:
    case ToolChain_WINCE:
    case ToolChain_OTHER:
    case ToolChain_UNKNOWN:
    case ToolChain_INVALID:
306
        if (!isCdbEngineEnabled()) {
307
308
            check.errorMessage = msgCdbDisabled(toolChain);
            check.settingsPage = CdbOptionsPage::settingsId();
309
310
311
312
        }
        break;
    default:
        //: %1 is something like "GCCE" or "Intel C++ Compiler (Linux)" (see ToolChain context)
313
314
315
        check.errorMessage = CdbEngine::tr("The CDB debug engine does not support the %1 toolchain.").
                    arg(ToolChain::toolChainName(toolChain));
        check.settingsPage = CdbOptionsPage::settingsId();
316
    }
317
    return check;
318
319
}

320
void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
321
{
322
#ifdef Q_OS_WIN
323
    opts->push_back(new CdbOptionsPage);
324
325
326
#else
    Q_UNUSED(opts);
#endif
327
328
329
330
331
332
}

#define QT_CREATOR_CDB_EXT "qtcreatorcdbext"

static inline Utils::SavedAction *theAssemblerAction()
{
333
    return debuggerCore()->action(OperateByInstruction);
334
335
}

336
337
338
CdbEngine::CdbEngine(const DebuggerStartParameters &sp,
        DebuggerEngine *masterEngine, const OptionsPtr &options) :
    DebuggerEngine(sp, masterEngine),
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    m_creatorExtPrefix("<qtcreatorcdbext>|"),
    m_tokenPrefix("<token>"),
    m_options(options),
    m_inferiorPid(0),
    m_accessible(false),
    m_specialStopMode(NoSpecialStop),
    m_nextCommandToken(0),
    m_nextBreakpointNumber(1),
    m_currentBuiltinCommandIndex(-1),
    m_extensionCommandPrefixBA("!"QT_CREATOR_CDB_EXT"."),
    m_operateByInstructionPending(true),
    m_operateByInstruction(true), // Default CDB setting
    m_notifyEngineShutdownOnTermination(false),
    m_hasDebuggee(false),
353
354
    m_elapsedLogTime(0),
    m_sourceStepInto(false)
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
{
    Utils::SavedAction *assemblerAction = theAssemblerAction();
    m_operateByInstructionPending = assemblerAction->isChecked();
    connect(assemblerAction, SIGNAL(triggered(bool)), this, SLOT(operateByInstructionTriggered(bool)));

    setObjectName(QLatin1String("CdbEngine"));
    connect(&m_process, SIGNAL(finished(int)), this, SLOT(processFinished()));
    connect(&m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError()));
    connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOut()));
    connect(&m_process, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardOut()));
}

CdbEngine::~CdbEngine()
{
}

void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
{
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
    if (state() == InferiorStopOk) {
        syncOperateByInstruction(operateByInstruction);
    } else {
        // To be set next time session becomes accessible
        m_operateByInstructionPending = operateByInstruction;
    }
}

void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
{
    if (m_operateByInstruction == operateByInstruction)
        return;
    QTC_ASSERT(m_accessible, return; )
    m_operateByInstruction = operateByInstruction;
    postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
    postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
389
390
391
392
}

void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
    if (debug)
        qDebug() << Q_FUNC_INFO;
    // Need a stopped debuggee and a cpp file in a valid frame
    if (state() != InferiorStopOk || !isCppEditor(editor) || stackHandler()->currentIndex() < 0)
        return;
    // Determine expression and function
    int line;
    int column;
    QString function;
    const QString exp = cppExpressionAt(editor, cursorPos, &line, &column, &function);
    // Are we in the current stack frame
    if (function.isEmpty() || exp.isEmpty() || function != stackHandler()->currentFrame().function)
        return;
    // No numerical or any other expressions [yet]
    if (!(exp.at(0).isLetter() || exp.at(0) == QLatin1Char('_')))
        return;
409
410
411
412
413
    const QByteArray iname = QByteArray(localsPrefixC) + exp.toAscii();
    if (const WatchData *data = watchHandler()->findItem(iname)) {
        QToolTip::hideText();
        QToolTip::showText(mousePos, data->toToolTip());
    }
414
415
416
417
}

void CdbEngine::setupEngine()
{
418
    // Nag to add symbol server
419
420
    if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(),
                                                         &(m_options->symbolPaths)))
421
422
        m_options->toSettings(Core::ICore::instance()->settings());

423
424
425
426
427
428
429
430
    QString errorMessage;
    if (!doSetupEngine(&errorMessage)) { // Start engine which will run until initial breakpoint
        showMessage(errorMessage, LogError);
        notifyEngineSetupFailed();
    }
}

// Determine full path to the CDB extension library.
431
QString CdbEngine::extensionLibraryName(bool is64Bit)
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
{
    // Determine extension lib name and path to use
    QString rc;
    QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
                     << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT"64" : QT_CREATOR_CDB_EXT"32")
                     << '/' << QT_CREATOR_CDB_EXT << ".dll";
    return rc;
}

// Determine environment for CDB.exe, start out with run config and
// add CDB extension path merged with system value should there be one.
static QStringList mergeEnvironment(QStringList runConfigEnvironment,
                                    QString cdbExtensionPath)
{
    // Determine CDB extension path from Qt Creator
    static const char cdbExtensionPathVariableC[] = "_NT_DEBUGGER_EXTENSION_PATH";
    const QByteArray oldCdbExtensionPath = qgetenv(cdbExtensionPathVariableC);
    if (!oldCdbExtensionPath.isEmpty()) {
        cdbExtensionPath.append(QLatin1Char(';'));
        cdbExtensionPath.append(QString::fromLocal8Bit(oldCdbExtensionPath));
    }
    // We do not assume someone sets _NT_DEBUGGER_EXTENSION_PATH in the run
    // config, just to make sure, delete any existing entries
    const QString cdbExtensionPathVariableAssign =
            QLatin1String(cdbExtensionPathVariableC) + QLatin1Char('=');
    for (QStringList::iterator it = runConfigEnvironment.begin(); it != runConfigEnvironment.end() ; ) {
        if (it->startsWith(cdbExtensionPathVariableAssign)) {
            it = runConfigEnvironment.erase(it);
            break;
        } else {
            ++it;
        }
    }
    runConfigEnvironment.append(cdbExtensionPathVariableAssign +
                                QDir::toNativeSeparators(cdbExtensionPath));
    return runConfigEnvironment;
}

int CdbEngine::elapsedLogTime() const
{
    const int elapsed = m_logTime.elapsed();
    const int delta = elapsed - m_elapsedLogTime;
    m_elapsedLogTime = elapsed;
    return delta;
}

bool CdbEngine::doSetupEngine(QString *errorMessage)
{
    m_logTime.start();
    // Determine extension lib name and path to use
    // The extension is passed as relative name with the path variable set
    //(does not work with absolute path names)
484
    const QFileInfo extensionFi(CdbEngine::extensionLibraryName(m_options->is64bit));
485
486
487
488
489
    if (!extensionFi.isFile()) {
        *errorMessage = QString::fromLatin1("Internal error: The extension %1 cannot be found.").
                arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
        return false;
    }
490
    const QString extensionFileName = extensionFi.fileName();
491
492
493
    // Prepare arguments
    const DebuggerStartParameters &sp = startParameters();
    QStringList arguments;
494
495
496
497
498
499
    const bool isRemote = sp.startMode == AttachToRemote;
    if (isRemote) { // Must be first
        arguments << QLatin1String("-remote") << sp.remoteChannel;
    } else {
        arguments << (QLatin1String("-a") + extensionFileName);
    }
500
501
502
503
504
505
506
507
508
509
510
511
512
513
    // Source line info/No terminal breakpoint / Pull extension
    arguments << QLatin1String("-lines") << QLatin1String("-G")
    // register idle (debuggee stop) notification
              << QLatin1String("-c")
              << QString::fromAscii(".idle_cmd " + m_extensionCommandPrefixBA + "idle");
    if (sp.useTerminal) // Separate console
        arguments << QLatin1String("-2");
    if (!m_options->symbolPaths.isEmpty())
        arguments << QLatin1String("-y") << m_options->symbolPaths.join(QString(QLatin1Char(';')));
    if (!m_options->sourcePaths.isEmpty())
        arguments << QLatin1String("-srcpath") << m_options->sourcePaths.join(QString(QLatin1Char(';')));
    switch (sp.startMode) {
    case StartInternal:
    case StartExternal:
514
        arguments << QDir::toNativeSeparators(sp.executable);
515
        break;
516
517
    case AttachToRemote:
        break;
518
    case AttachExternal:
519
    case AttachCrashedExternal:
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
        arguments << QLatin1String("-p") << QString::number(sp.attachPID);
        if (sp.startMode == AttachCrashedExternal)
            arguments << QLatin1String("-e") << sp.crashParameter << QLatin1String("-g");
        break;
    default:
        *errorMessage = QString::fromLatin1("Internal error: Unsupported start mode %1.").arg(sp.startMode);
        return false;
    }
    const QString executable = m_options->executable;
    const QString msg = QString::fromLatin1("Launching %1 %2\nusing %3 of %4.").
            arg(QDir::toNativeSeparators(executable),
                arguments.join(QString(QLatin1Char(' '))),
                QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
                extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
    showMessage(msg, LogMisc);

    m_outputBuffer.clear();
    m_process.setEnvironment(mergeEnvironment(sp.environment.toStringList(), extensionFi.absolutePath()));
538
539
540
541
#ifdef Q_OS_WIN
    if (!sp.processArgs.isEmpty()) // Appends
        m_process.setNativeArguments(sp.processArgs);
#endif
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    m_process.start(executable, arguments);
    if (!m_process.waitForStarted()) {
        *errorMessage = QString::fromLatin1("Internal error: Cannot start process %1: %2").
                arg(QDir::toNativeSeparators(executable), m_process.errorString());
        return false;
    }
#ifdef Q_OS_WIN
    const unsigned long pid = Utils::winQPidToPid(m_process.pid());
#else
    const unsigned long pid = 0;
#endif
    showMessage(QString::fromLatin1("%1 running as %2").
                arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
    m_hasDebuggee = true;
556
557
558
559
560
561
562
    if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
        m_accessible = true;
        const QByteArray loadCommand = QByteArray(".load ")
                + extensionFileName.toLocal8Bit();
        postCommand(loadCommand, 0);
        notifyEngineSetupOk();
    }
563
564
565
566
567
    return true;
}

void CdbEngine::setupInferior()
{
568
569
    if (debug)
        qDebug("setupInferior");
570
571
572
573
574
575
    attemptBreakpointSynchronization();
    postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
}

void CdbEngine::runEngine()
{
576
577
    if (debug)
        qDebug("runEngine");
578
579
    foreach (const QString &breakEvent, m_options->breakEvents)
            postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0);
580
581
582
    postCommand("g", 0);
}

583
584
585
586
587
bool CdbEngine::commandsPending() const
{
    return !m_builtinCommandQueue.isEmpty() || !m_extensionCommandQueue.isEmpty();
}

588
589
590
591
592
593
594
595
596
597
598
599
void CdbEngine::shutdownInferior()
{
    if (debug)
        qDebug("CdbEngine::shutdownInferior in state '%s', process running %d", stateName(state()),
               isCdbProcessRunning());

    if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
        if (debug)
            qDebug("notifyInferiorShutdownOk");
        notifyInferiorShutdownOk();
        return;
    }
600
    if (!canInterruptInferior() || commandsPending()) {
601
602
603
604
        notifyInferiorShutdownFailed();
        return;
    }

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
    if (m_accessible) {
        if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal)
            detachDebugger();
        if (debug)
            qDebug("notifyInferiorShutdownOk");
        notifyInferiorShutdownOk();
    } else {
        interruptInferior(); // Calls us again
    }
}

/* shutdownEngine/processFinished:
 * Note that in the case of launching a process by the debugger, the debugger
 * automatically quits a short time after reporting the session becoming
 * inaccessible without debuggee (notifyInferiorExited). In that case,
 * processFinished() must not report any arbitrarily notifyEngineShutdownOk()
 * as not to confuse the state engine.
 */

void CdbEngine::shutdownEngine()
{
    if (debug)
627
628
629
630
        qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
               "accessible=%d,commands pending=%d",
               stateName(state()), isCdbProcessRunning(), m_accessible,
               commandsPending());
631
632
633
634
635
636
637
638

    if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
        if (debug)
            qDebug("notifyEngineShutdownOk");
        notifyEngineShutdownOk();
        return;
    }

639
640
641
642
643
644
    // No longer trigger anything from messages
    disconnect(&m_process, SIGNAL(readyReadStandardOutput()), this, 0);
    disconnect(&m_process, SIGNAL(readyReadStandardError()), this, 0);
    // Go for kill if there are commands pending.
    if (m_accessible && !commandsPending()) {
        // detach: Wait for debugger to finish.
645
646
        if (startParameters().startMode == AttachExternal)
            detachDebugger();
647
648
649
650
651
652
653
654
655
656
657
        // Remote requires a bit more force to quit.
        if (startParameters().startMode == AttachToRemote) {
            postCommand(m_extensionCommandPrefixBA + "shutdownex", 0);
            postCommand("qq", 0);
        } else {
            postCommand("q", 0);
        }
        m_notifyEngineShutdownOnTermination = true;
        return;
    } else {
        // Remote process. No can do, currently
658
        m_notifyEngineShutdownOnTermination = true;
659
        Utils::SynchronousProcess::stopProcess(m_process);
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
        return;
    }
    // Lost debuggee, debugger should quit anytime now
    if (!m_hasDebuggee) {
        m_notifyEngineShutdownOnTermination = true;
        return;
    }
    interruptInferior();
}

void CdbEngine::processFinished()
{
    if (debug)
        qDebug("CdbEngine::processFinished %dms '%s' notify=%d (exit state=%d, ex=%d)",
               elapsedLogTime(), stateName(state()), m_notifyEngineShutdownOnTermination,
               m_process.exitStatus(), m_process.exitCode());

    const bool crashed = m_process.exitStatus() == QProcess::CrashExit;
    if (crashed) {
        showMessage(tr("CDB crashed"), LogError); // not in your life.
    } else {
        showMessage(tr("CDB exited (%1)").arg(m_process.exitCode()), LogMisc);
    }

    if (m_notifyEngineShutdownOnTermination) {
        if (crashed) {
            if (debug)
                qDebug("notifyEngineIll");
            notifyEngineIll();
        } else {
            if (debug)
                qDebug("notifyEngineShutdownOk");
            notifyEngineShutdownOk();
        }
    } else {
        if (debug)
            qDebug("notifyEngineSpontaneousShutdown");
        notifyEngineSpontaneousShutdown();
    }
}

void CdbEngine::detachDebugger()
{
    postCommand(".detach", 0);
}

706
707
void CdbEngine::updateWatchData(const WatchData &dataIn,
                                const WatchUpdateFlags & flags)
708
709
710
711
712
713
714
715
{
    if (debug)
        qDebug("CdbEngine::updateWatchData() %dms %s incr=%d: %s",
               elapsedLogTime(), stateName(state()),
               flags.tryIncremental,
               qPrintable(dataIn.toString()));

    if (!dataIn.hasChildren) {
716
        WatchData data = dataIn;
717
718
719
720
721
722
723
        data.setAllUnneeded();
        watchHandler()->insertData(data);
        return;
    }
    updateLocalVariable(dataIn.iname);
}

724
725
void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
{
726
727
    if (debuggerCore()->boolSetting(VerboseLog))
        str << blankSeparator << "-v";
728
729
730
731
732
733
734
735
736
737
    if (debuggerCore()->boolSetting(UseDebuggingHelpers))
        str << blankSeparator << "-c";
    const QByteArray typeFormats = watchHandler()->typeFormatRequests();
    if (!typeFormats.isEmpty())
        str << blankSeparator << "-T " << typeFormats;
    const QByteArray individualFormats = watchHandler()->individualFormatRequests();
    if (!individualFormats.isEmpty())
        str << blankSeparator << "-I " << individualFormats;
}

738
739
740
741
742
743
void CdbEngine::updateLocalVariable(const QByteArray &iname)
{
    const int stackFrame = stackHandler()->currentIndex();
    if (stackFrame >= 0) {
        QByteArray localsArguments;
        ByteArrayInputStream str(localsArguments);
744
745
        addLocalsOptions(str);
        str << blankSeparator << stackFrame <<  ' ' << iname;
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
        postExtensionCommand("locals", localsArguments, 0, &CdbEngine::handleLocals);
    } else {
        qWarning("Internal error; no stack frame in updateLocalVariable");
    }
}

unsigned CdbEngine::debuggerCapabilities() const
{
    return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
           |WatchpointCapability|JumpToLineCapability
           |BreakOnThrowAndCatchCapability; // Sort-of: Can break on throw().
}

void CdbEngine::executeStep()
{
    postCommand(QByteArray("t"), 0); // Step into-> t (trace)
762
763
    if (!m_operateByInstruction)
        m_sourceStepInto = true; // See explanation at handleStackTrace().
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
    notifyInferiorRunRequested();
}

void CdbEngine::executeStepOut()
{
    postCommand(QByteArray("gu"), 0); // Step out-> gu (go up)
    notifyInferiorRunRequested();
}

void CdbEngine::executeNext()
{
    postCommand(QByteArray("p"), 0); // Step over -> p
    notifyInferiorRunRequested();
}

void CdbEngine::executeStepI()
{
    executeStep();
}

void CdbEngine::executeNextI()
{
    executeNext();
}

void CdbEngine::continueInferior()
{
    notifyInferiorRunRequested();
    doContinueInferior();
}

void CdbEngine::doContinueInferior()
{
    postCommand(QByteArray("g"), 0);
}

800
801
802
803
804
bool CdbEngine::canInterruptInferior() const
{
    return startParameters().startMode != AttachToRemote;
}

805
806
void CdbEngine::interruptInferior()
{
807
808
809
810
811
812
813
814
    if (canInterruptInferior()) {
        doInterruptInferior(NoSpecialStop);
    } else {
        showMessage(tr("Interrupting is not possible in remote sessions."), LogError);
        notifyInferiorStopOk();
        notifyInferiorRunRequested();
        notifyInferiorRunOk();
    }
815
816
817
818
819
820
821
822
}

void CdbEngine::doInterruptInferior(SpecialStopMode sm)
{
#ifdef Q_OS_WIN
    const SpecialStopMode oldSpecialMode = m_specialStopMode;
    m_specialStopMode = sm;
    QString errorMessage;
823
    if (!winDebugBreakProcess(m_inferiorPid, &errorMessage)) {
824
825
826
827
828
829
830
831
832
833
834
        m_specialStopMode = oldSpecialMode;
        showMessage(errorMessage, LogError);
    }
#else
    Q_UNUSED(sm)
#endif
}

void CdbEngine::executeRunToLine(const QString &fileName, int lineNumber)
{
    // Add one-shot breakpoint
835
    BreakpointParameters bp(BreakpointByFileAndLine);
836
837
838
839
840
841
842
843
844
    bp.fileName = fileName;
    bp.lineNumber = lineNumber;
    postCommand(cdbAddBreakpointCommand(bp, true), 0);
    continueInferior();
}

void CdbEngine::executeRunToFunction(const QString &functionName)
{
    // Add one-shot breakpoint
845
    BreakpointParameters bp(BreakpointByFunction);
846
847
848
849
850
851
852
853
    bp.functionName = functionName;

    postCommand(cdbAddBreakpointCommand(bp, true), 0);
    continueInferior();
}

void CdbEngine::setRegisterValue(int regnr, const QString &value)
{
854
    const Registers registers = registerHandler()->registers();
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
    QTC_ASSERT(regnr < registers.size(), return)
    // Value is decimal or 0x-hex-prefixed
    QByteArray cmd;
    ByteArrayInputStream str(cmd);
    str << "r " << registers.at(regnr).name << '=' << value;
    postCommand(cmd, 0);
    reloadRegisters();
}

void CdbEngine::executeJumpToLine(const QString & fileName, int lineNumber)
{
    QByteArray cmd;
    ByteArrayInputStream str(cmd);
    // Resolve source line address and go to that location
    str << "? `" << QDir::toNativeSeparators(fileName) << ':' << lineNumber << '`';
    const QVariant cookie = qVariantFromValue(SourceLocationCookie(fileName, lineNumber));
    postBuiltinCommand(cmd, 0, &CdbEngine::handleJumpToLineAddressResolution, 0, cookie);
}

void CdbEngine::handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &cmd)
{
    if (cmd->reply.isEmpty())
        return;
    // Evaluate expression: 5365511549 = 00000001`3fcf357d
    // Set register 'rip' to hex address and goto lcoation
    QByteArray answer = cmd->reply.front();
    const int equalPos = answer.indexOf(" = ");
    if (equalPos == -1)
        return;
    answer.remove(0, equalPos + 3);
    QTC_ASSERT(qVariantCanConvert<SourceLocationCookie>(cmd->cookie), return ; )
    const SourceLocationCookie cookie = qvariant_cast<SourceLocationCookie>(cmd->cookie);

    QByteArray registerCmd;
    ByteArrayInputStream str(registerCmd);
890
891
    // PC-register depending on 64/32bit.
    str << "r " << (m_options->is64bit ? "rip" : "eip") << "=0x" << answer;
892
    postCommand(registerCmd, 0);
893
    gotoLocation(Location(cookie.fileName, cookie.lineNumber));
894
895
}

896
void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, const QVariant &value)
897
898
899
900
901
902
903
904
905
906
907
908
909
{
    if (debug)
        qDebug() << "CdbEngine::assignValueInDebugger" << w->iname << expr << value;

    if (state() != InferiorStopOk || stackHandler()->currentIndex() < 0) {
        qWarning("Internal error: assignValueInDebugger: Invalid state or no stack frame.");
        return;
    }

    QByteArray cmd;
    ByteArrayInputStream str(cmd);
    str << m_extensionCommandPrefixBA << "assign " << w->iname << '=' << value.toString();
    postCommand(cmd, 0);
910
911
912
    // Update all locals in case we change a union or something pointed to
    // that affects other variables, too.
    updateLocals();
913
914
915
916
917
918
919
}

void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
{
    if (debug)
        qDebug("CdbEngine::handleThreads success=%d", reply->success);
    if (reply->success) {
920
        GdbMi data;
921
922
        data.fromString(reply->reply);
        int currentThreadId;
923
        Threads threads = ThreadsHandler::parseGdbmiThreads(data, &currentThreadId);
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
        threadsHandler()->setThreads(threads);
        threadsHandler()->setCurrentThreadId(currentThreadId);
        // Continue sequence
        postCommandSequence(reply->commandSequence);
    } else {
        showMessage(QString::fromLatin1(reply->errorMessage), LogError);
    }
}

void CdbEngine::executeDebuggerCommand(const QString &command)
{
    postCommand(command.toLocal8Bit(), QuietCommand);
}

// Post command without callback
void CdbEngine::postCommand(const QByteArray &cmd, unsigned flags)
{
    if (debug)
        qDebug("CdbEngine::postCommand %dms '%s' %u %s\n",
               elapsedLogTime(), cmd.constData(), flags, stateName(state()));
    if (!(flags & QuietCommand))
        showMessage(QString::fromLocal8Bit(cmd), LogInput);
    m_process.write(cmd + '\n');
}

// Post a built-in-command producing free-format output with a callback.
// In order to catch the output, it is enclosed in 'echo' commands
// printing a specially formatted token to be identifiable in the output.
void CdbEngine::postBuiltinCommand(const QByteArray &cmd, unsigned flags,
                                   BuiltinCommandHandler handler,
                                   unsigned nextCommandFlag,
                                   const QVariant &cookie)
{
    if (!m_accessible) {
        const QString msg = QString::fromLatin1("Attempt to issue builtin command '%1' to non-accessible session (%2)")
                .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
        showMessage(msg, LogError);
        return;
    }
    if (!flags & QuietCommand)
        showMessage(QString::fromLocal8Bit(cmd), LogInput);

    const int token = m_nextCommandToken++;
    CdbBuiltinCommandPtr pendingCommand(new CdbBuiltinCommand(cmd, token, flags, handler, nextCommandFlag, cookie));

    m_builtinCommandQueue.push_back(pendingCommand);
    // Enclose command in echo-commands for token
    QByteArray fullCmd;
    ByteArrayInputStream str(fullCmd);
    str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
            << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"\n";
    if (debug)
        qDebug("CdbEngine::postBuiltinCommand %dms '%s' flags=%u token=%d %s next=%u, cookie='%s', pending=%d, sequence=0x%x",
               elapsedLogTime(), cmd.constData(), flags, token, stateName(state()), nextCommandFlag, qPrintable(cookie.toString()),
               m_builtinCommandQueue.size(), nextCommandFlag);
    if (debug > 1)
        qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
               fullCmd.constData());
    m_process.write(fullCmd);
}

// Post an extension command producing one-line output with a callback,
// pass along token for identification in queue.
void CdbEngine::postExtensionCommand(const QByteArray &cmd,
                                     const QByteArray &arguments,
                                     unsigned flags,
                                     ExtensionCommandHandler handler,
                                     unsigned nextCommandFlag,
                                     const QVariant &cookie)
{
    if (!m_accessible) {
        const QString msg = QString::fromLatin1("Attempt to issue extension command '%1' to non-accessible session (%2)")
                .arg(QString::fromLocal8Bit(cmd), QString::fromAscii(stateName(state())));
        showMessage(msg, LogError);
        return;
    }

For faster browsing, not all history is shown. View entire blame