cdbengine.cpp 120 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12
13
14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16
17
18
19
20
21
22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
25

26
#include "cdbengine.h"
hjk's avatar
hjk committed
27

hjk's avatar
hjk committed
28
#include "stringinputstream.h"
29
#include "cdboptionspage.h"
hjk's avatar
hjk committed
30
#include "cdbparsehelpers.h"
31
32
33
34

#include <debugger/breakhandler.h>
#include <debugger/debuggeractions.h>
#include <debugger/debuggercore.h>
35
#include <debugger/debuggerinternalconstants.h>
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <debugger/debuggerprotocol.h>
#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggertooltipmanager.h>
#include <debugger/disassembleragent.h>
#include <debugger/disassemblerlines.h>
#include <debugger/memoryagent.h>
#include <debugger/moduleshandler.h>
#include <debugger/registerhandler.h>
#include <debugger/stackhandler.h>
#include <debugger/threadshandler.h>
#include <debugger/watchhandler.h>
#include <debugger/procinterrupt.h>
#include <debugger/sourceutils.h>
#include <debugger/shared/cdbsymbolpathlisteditor.h>
#include <debugger/shared/hostutils.h>
52
53

#include <coreplugin/icore.h>
54
#include <coreplugin/messagebox.h>
55
#include <projectexplorer/taskhub.h>
56
#include <texteditor/texteditor.h>
57

58
#include <utils/synchronousprocess.h>
59
#include <utils/qtcprocess.h>
60
61
62
#include <utils/winutils.h>
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
63
#include <utils/consoleprocess.h>
64
#include <utils/fileutils.h>
65
#include <utils/hostosinfo.h>
66
67

#include <cplusplus/findcdbbreakpoint.h>
68
#include <cpptools/cppmodelmanager.h>
69
#include <cpptools/cppworkingcopy.h>
70

71
#include <QDir>
72
73
74
75

#include <cctype>

enum { debug = 0 };
76
enum { debugLocals = 0 };
77
enum { debugSourceMapping = 0 };
78
enum { debugWatches = 0 };
79
80
enum { debugBreakpoints = 0 };

81
#define CB(callback) [this](const DebuggerResponse &r) { callback(r); }
82

83
#if 0
84
#  define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line);
85
#else
86
#  define STATE_DEBUG(state, func, line, notifyFunc)
87
88
#endif

89
90
91
92
93
94
95
96
/*!
    \class Debugger::Internal::CdbEngine

    Cdb engine 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
    library (32/64bit), which is loaded into cdb.exe. It serves to:

    \list
97
    \li Notify the engine about the state of the debugging session:
98
        \list
99
100
101
102
        \li idle: (hooked up with .idle_cmd) debuggee stopped
        \li accessible: Debuggee stopped, cdb.exe accepts commands
        \li inaccessible: Debuggee runs, no way to post commands
        \li session active/inactive: Lost debuggee, terminating.
103
        \endlist
104
    \li Hook up with output/event callbacks and produce formatted output to be able
105
       to catch application output and exceptions.
106
    \li Provide some extension commands that produce output in a standardized (GDBMI)
107
108
      format that ends up in handleExtensionMessage(), for example:
      \list
109
110
111
112
      \li pid     Return debuggee pid for interrupting.
      \li locals  Print locals from SymbolGroup
      \li expandLocals Expand locals in symbol group
      \li registers, modules, threads
113
114
115
116
117
118
119
      \endlist
   \endlist

   Debugger commands can be posted by calling:

   \list

120
    \li postCommand(): Does not expect a response
121
    \li postBuiltinCommand(): Run a builtin-command producing free-format, multiline output
122
123
       that is captured by enclosing it in special tokens using the 'echo' command and
       then invokes a callback with a CdbBuiltinCommand structure.
124
    \li postExtensionCommand(): Run a command provided by the extension producing
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
       one-line output and invoke a callback with a CdbExtensionCommand structure
       (output is potentially split up in chunks).
    \endlist


    Startup sequence:
    [Console: The console stub launches the process. On process startup,
              launchCDB() is called with AttachExternal].
    setupEngine() calls launchCDB() with the startparameters. The debuggee
    runs into the initial breakpoint (session idle). EngineSetupOk is
    notified (inferior still stopped). setupInferior() is then called
    which does breakpoint synchronization and issues the extension 'pid'
    command to obtain the inferior pid (which also hooks up the output callbacks).
     handlePid() notifies notifyInferiorSetupOk.
    runEngine() is then called which issues 'g' to continue the inferior.
    Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits
    when the inferior exits (except attach modes).
*/
143

144
using namespace ProjectExplorer;
145
using namespace Utils;
146

147
namespace Debugger {
148
namespace Internal {
149

150
151
static const char localsPrefixC[] = "local.";

hjk's avatar
hjk committed
152
153
struct MemoryViewCookie
{
154
155
    explicit MemoryViewCookie(MemoryAgent *a = 0, quint64 addr = 0, quint64 l = 0)
        : agent(a), address(addr), length(l)
hjk's avatar
hjk committed
156
    {}
157

158
    MemoryAgent *agent;
159
160
161
162
    quint64 address;
    quint64 length;
};

163
164
165
166
167
168
169
170
171
struct MemoryChangeCookie
{
    explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
                               address(addr), data(d) {}

    quint64 address;
    QByteArray data;
};

172
} // namespace Internal
173
174
} // namespace Debugger

175
Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
176
Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
177
178

namespace Debugger {
179
namespace Internal {
180

181
static inline bool isCreatorConsole(const DebuggerRunParameters &sp)
hjk's avatar
hjk committed
182
{
hjk's avatar
hjk committed
183
    return !boolSetting(UseCdbConsole) && sp.useTerminal
184
           && (sp.startMode == StartInternal || sp.startMode == StartExternal);
hjk's avatar
hjk committed
185
}
186

187
// Base data structure for command queue entries with callback
188
class CdbCommand
189
{
190
public:
191
192
    CdbCommand() {}
    CdbCommand(CdbEngine::CommandHandler h) : handler(h) {}
193

194
    CdbEngine::CommandHandler handler;
195
196
197
198
};

static inline bool validMode(DebuggerStartMode sm)
{
199
    return sm != NoStartMode;
200
201
202
}

// Accessed by RunControlFactory
203
DebuggerEngine *createCdbEngine(const DebuggerRunParameters &rp, QStringList *errors)
204
{
205
    if (HostOsInfo::isWindowsHost()) {
206
207
        if (validMode(rp.startMode))
            return new CdbEngine(rp);
208
        errors->append(CdbEngine::tr("Internal error: Invalid start parameters passed for the CDB engine."));
209
    } else {
210
        errors->append(CdbEngine::tr("Unsupported CDB host system."));
211
    }
212
213
214
    return 0;
}

215
void addCdbOptionPages(QList<Core::IOptionsPage *> *opts)
216
{
217
    if (HostOsInfo::isWindowsHost()) {
218
        opts->push_back(new CdbOptionsPage);
219
220
        opts->push_back(new CdbPathsPage);
    }
221
222
223
224
}

#define QT_CREATOR_CDB_EXT "qtcreatorcdbext"

225
CdbEngine::CdbEngine(const DebuggerRunParameters &sp) :
226
    DebuggerEngine(sp),
227
    m_tokenPrefix("<token>"),
228
    m_effectiveStartMode(NoStartMode),
229
230
231
    m_accessible(false),
    m_specialStopMode(NoSpecialStop),
    m_nextCommandToken(0),
232
    m_currentBuiltinResponseToken(-1),
hjk's avatar
hjk committed
233
    m_extensionCommandPrefix("!" QT_CREATOR_CDB_EXT "."),
234
235
236
    m_operateByInstructionPending(true),
    m_operateByInstruction(true), // Default CDB setting
    m_hasDebuggee(false),
237
    m_wow64State(wow64Uninitialized),
238
    m_elapsedLogTime(0),
239
    m_sourceStepInto(false),
240
    m_watchPointX(0),
241
242
    m_watchPointY(0),
    m_ignoreCdbOutput(false)
243
{
hjk's avatar
hjk committed
244
    setObjectName("CdbEngine");
hjk's avatar
hjk committed
245
246
247
248
249
250
251

    connect(action(OperateByInstruction), &QAction::triggered,
            this, &CdbEngine::operateByInstructionTriggered);
    connect(action(CreateFullBacktrace), &QAction::triggered,
            this, &CdbEngine::createFullBacktrace);
    connect(&m_process, static_cast<void(QProcess::*)(int)>(&QProcess::finished),
            this, &CdbEngine::processFinished);
252
    connect(&m_process, &QProcess::errorOccurred, this, &CdbEngine::processError);
hjk's avatar
hjk committed
253
254
255
256
    connect(&m_process, &QProcess::readyReadStandardOutput,
            this, &CdbEngine::readyReadStandardOut);
    connect(&m_process, &QProcess::readyReadStandardError,
            this, &CdbEngine::readyReadStandardOut);
257
258
    connect(action(UseDebuggingHelpers), &SavedAction::valueChanged,
            this, &CdbEngine::updateLocals);
259
260
}

261
262
263
void CdbEngine::init()
{
    m_effectiveStartMode = NoStartMode;
264
    notifyInferiorPid(0);
265
266
267
    m_accessible = false;
    m_specialStopMode = NoSpecialStop;
    m_nextCommandToken  = 0;
268
    m_currentBuiltinResponseToken = -1;
hjk's avatar
hjk committed
269
    m_operateByInstructionPending = action(OperateByInstruction)->isChecked();
270
271
272
273
274
    m_operateByInstruction = true; // Default CDB setting
    m_hasDebuggee = false;
    m_sourceStepInto = false;
    m_watchPointX = m_watchPointY = 0;
    m_ignoreCdbOutput = false;
275
    m_autoBreakPointCorrection = false;
276
    m_wow64State = wow64Uninitialized;
277
278

    m_outputBuffer.clear();
279
    m_commandForToken.clear();
280
    m_currentBuiltinResponse.clear();
281
282
    m_extensionMessageBuffer.clear();
    m_pendingBreakpointMap.clear();
283
284
    m_insertSubBreakpointMap.clear();
    m_pendingSubBreakpointMap.clear();
285
    m_customSpecialStopData.clear();
286
    m_symbolAddressCache.clear();
287
    m_coreStopReason.reset();
288
289
290

    // Create local list of mappings in native separators
    m_sourcePathMappings.clear();
291
    const QSharedPointer<GlobalDebuggerOptions> globalOptions = Internal::globalDebuggerOptions();
Orgad Shaneh's avatar
Orgad Shaneh committed
292
293
294
295
    SourcePathMap sourcePathMap = globalOptions->sourcePathMap;
    if (!sourcePathMap.isEmpty()) {
        m_sourcePathMappings.reserve(sourcePathMap.size());
        for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; ++it) {
296
297
298
299
            m_sourcePathMappings.push_back(SourcePathMapping(QDir::toNativeSeparators(it.key()),
                                                             QDir::toNativeSeparators(it.value())));
        }
    }
300
301
    // update source path maps from debugger start params
    mergeStartParametersSourcePathMap();
302
    QTC_ASSERT(m_process.state() != QProcess::Running, SynchronousProcess::stopProcess(m_process));
303
304
}

305
306
307
308
309
310
CdbEngine::~CdbEngine()
{
}

void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
{
311
312
313
    // To be set next time session becomes accessible
    m_operateByInstructionPending = operateByInstruction;
    if (state() == InferiorStopOk)
314
315
316
317
318
        syncOperateByInstruction(operateByInstruction);
}

void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
{
319
320
    if (debug)
        qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
321
322
    if (m_operateByInstruction == operateByInstruction)
        return;
323
    QTC_ASSERT(m_accessible, return);
324
    m_operateByInstruction = operateByInstruction;
hjk's avatar
hjk committed
325
326
    runCommand({QLatin1String(m_operateByInstruction ? "l-t" : "l+t"), NoFlags});
    runCommand({QLatin1String(m_operateByInstruction ? "l-s" : "l+s"), NoFlags});
327
328
}

329
bool CdbEngine::canHandleToolTip(const DebuggerToolTipContext &context) const
330
{
hjk's avatar
hjk committed
331
332
333
334
335
    Q_UNUSED(context);
    // Tooltips matching local variables are already handled in the
    // base class. We don't handle anything else here in CDB
    // as it can slow debugging down.
    return false;
336
337
338
}

// Determine full path to the CDB extension library.
339
QString CdbEngine::extensionLibraryName(bool is64Bit)
340
341
342
343
{
    // Determine extension lib name and path to use
    QString rc;
    QTextStream(&rc) << QFileInfo(QCoreApplication::applicationDirPath()).path()
hjk's avatar
hjk committed
344
                     << "/lib/" << (is64Bit ? QT_CREATOR_CDB_EXT "64" : QT_CREATOR_CDB_EXT "32")
345
346
347
348
349
350
351
352
353
354
355
356
357
                     << '/' << 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()) {
hjk's avatar
hjk committed
358
        cdbExtensionPath.append(';');
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
        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;
}

386
// Start the console stub with the sub process. Continue in consoleStubProcessStarted.
387
bool CdbEngine::startConsole(const DebuggerRunParameters &sp, QString *errorMessage)
388
389
{
    if (debug)
390
        qDebug("startConsole %s", qPrintable(sp.inferior.executable));
391
392
    m_consoleStub.reset(new ConsoleProcess);
    m_consoleStub->setMode(ConsoleProcess::Suspend);
393
394
395
396
397
398
    connect(m_consoleStub.data(), &ConsoleProcess::processError,
            this, &CdbEngine::consoleStubError);
    connect(m_consoleStub.data(), &ConsoleProcess::processStarted,
            this, &CdbEngine::consoleStubProcessStarted);
    connect(m_consoleStub.data(), &ConsoleProcess::stubStopped,
            this, &CdbEngine::consoleStubExited);
399
    m_consoleStub->setWorkingDirectory(sp.inferior.workingDirectory);
400
401
    if (sp.stubEnvironment.size())
        m_consoleStub->setEnvironment(sp.stubEnvironment);
402
403
    if (!m_consoleStub->start(sp.inferior.executable, sp.inferior.commandLineArguments)) {
        *errorMessage = tr("The console process \"%1\" could not be started.").arg(sp.inferior.executable);
404
405
406
407
408
        return false;
    }
    return true;
}

409
void CdbEngine::consoleStubError(const QString &msg)
410
{
411
    if (debug)
hjk's avatar
hjk committed
412
        qDebug("consoleStubProcessMessage() in %s %s", qPrintable(stateName(state())), qPrintable(msg));
413
414
415
    if (state() == EngineSetupRequested) {
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
        notifyEngineSetupFailed();
416
    } else {
417
418
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll")
        notifyEngineIll();
419
    }
420
    Core::AsynchronousMessageBox::critical(tr("Debugger Error"), msg);
421
422
423
424
425
426
427
}

void CdbEngine::consoleStubProcessStarted()
{
    if (debug)
        qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID());
    // Attach to console process.
428
    DebuggerRunParameters attachParameters = runParameters();
429
430
    attachParameters.inferior.executable.clear();
    attachParameters.inferior.commandLineArguments.clear();
431
432
    attachParameters.attachPID = m_consoleStub->applicationPID();
    attachParameters.startMode = AttachExternal;
433
    attachParameters.useTerminal = false;
hjk's avatar
hjk committed
434
    showMessage(QString("Attaching to %1...").arg(attachParameters.attachPID), LogMisc);
435
436
437
    QString errorMessage;
    if (!launchCDB(attachParameters, &errorMessage)) {
        showMessage(errorMessage, LogError);
438
        Core::AsynchronousMessageBox::critical(tr("Failed to Start the Debugger"), errorMessage);
439
440
441
442
443
444
445
446
447
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
        notifyEngineSetupFailed();
    }
}

void CdbEngine::consoleStubExited()
{
}

448
449
void CdbEngine::createFullBacktrace()
{
450
    runCommand({"~*kp", BuiltinCommand, [this](const DebuggerResponse &response) {
hjk's avatar
hjk committed
451
        Internal::openTextEditor("Backtrace $", response.data.data());
452
    }});
453
454
}

455
456
457
458
459
void CdbEngine::setupEngine()
{
    if (debug)
        qDebug(">setupEngine");

460
461
462
    init();
    if (!m_logTime.elapsed())
        m_logTime.start();
463
464
465
466
467
    QString errorMessage;
    // Console: Launch the stub with the suspended application and attach to it
    // CDB in theory has a command line option '-2' that launches a
    // console, too, but that immediately closes when the debuggee quits.
    // Use the Creator stub instead.
468
469
470
    const DebuggerRunParameters &rp = runParameters();
    const bool launchConsole = isCreatorConsole(rp);
    m_effectiveStartMode = launchConsole ? AttachExternal : rp.startMode;
471
    const bool ok = launchConsole ?
472
473
                startConsole(runParameters(), &errorMessage) :
                launchCDB(runParameters(), &errorMessage);
474
475
476
477
    if (debug)
        qDebug("<setupEngine ok=%d", ok);
    if (!ok) {
        showMessage(errorMessage, LogError);
478
        Core::AsynchronousMessageBox::critical(tr("Failed to Start the Debugger"), errorMessage);
479
480
481
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed")
        notifyEngineSetupFailed();
    }
482
483
484
485
486

    DisplayFormats stringFormats;
    stringFormats.append(SimpleFormat);
    stringFormats.append(SeparateFormat);

487
488
489
490
491
    WatchHandler *wh = watchHandler();
    wh->addTypeFormats("QString", stringFormats);
    wh->addTypeFormats("QString *", stringFormats);
    wh->addTypeFormats("QByteArray", stringFormats);
    wh->addTypeFormats("QByteArray *", stringFormats);
492
    wh->addTypeFormats("std__basic_string", stringFormats);  // Python dumper naming convention for std::[w]string
493
494
495
496

    DisplayFormats imageFormats;
    imageFormats.append(SimpleFormat);
    imageFormats.append(EnhancedFormat);
497
498
    wh->addTypeFormats("QImage", imageFormats);
    wh->addTypeFormats("QImage *", imageFormats);
499
500
}

501
bool CdbEngine::launchCDB(const DebuggerRunParameters &sp, QString *errorMessage)
502
503
504
{
    if (debug)
        qDebug("launchCDB startMode=%d", sp.startMode);
hjk's avatar
hjk committed
505
    const QChar blank(' ');
506
    // Start engine which will run until initial breakpoint:
507
    // Determine binary (force MSVC), extension lib name and path to use
508
509
    // The extension is passed as relative name with the path variable set
    //(does not work with absolute path names)
510
    const QString executable = sp.debugger.executable;
511
512
513
514
515
    if (executable.isEmpty()) {
        *errorMessage = tr("There is no CDB executable specified.");
        return false;
    }

hjk's avatar
hjk committed
516
517
    bool cdbIs64Bit = Utils::is64BitWindowsBinary(executable);
    if (!cdbIs64Bit)
518
        m_wow64State = noWow64Stack;
hjk's avatar
hjk committed
519
    const QFileInfo extensionFi(CdbEngine::extensionLibraryName(cdbIs64Bit));
520
    if (!extensionFi.isFile()) {
hjk's avatar
hjk committed
521
522
523
        *errorMessage = QString("Internal error: The extension %1 cannot be found.\n"
                                "If you build Qt Creator from sources, check out "
                                "https://code.qt.io/cgit/qt-creator/binary-artifacts.git/.").
524
525
526
                arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
        return false;
    }
527
    const QString extensionFileName = extensionFi.fileName();
528
529
    // Prepare arguments
    QStringList arguments;
530
    const bool isRemote = sp.startMode == AttachToRemoteServer;
531
    if (isRemote) { // Must be first
hjk's avatar
hjk committed
532
        arguments << "-remote" << sp.remoteChannel;
533
    } else {
hjk's avatar
hjk committed
534
        arguments << ("-a" + extensionFileName);
535
    }
536
    // Source line info/No terminal breakpoint / Pull extension
hjk's avatar
hjk committed
537
    arguments << "-lines" << "-G"
538
    // register idle (debuggee stop) notification
hjk's avatar
hjk committed
539
540
              << "-c"
              << ".idle_cmd " + m_extensionCommandPrefix + "idle";
541
    if (sp.useTerminal) // Separate console
hjk's avatar
hjk committed
542
        arguments << "-2";
hjk's avatar
hjk committed
543
    if (boolSetting(IgnoreFirstChanceAccessViolation))
hjk's avatar
hjk committed
544
        arguments << "-x";
545

hjk's avatar
hjk committed
546
    const QStringList &sourcePaths = stringListSetting(CdbSourcePaths);
547
    if (!sourcePaths.isEmpty())
hjk's avatar
hjk committed
548
        arguments << "-srcpath" << sourcePaths.join(';');
549

550
    // Compile argument string preserving quotes
551
    QString nativeArguments = expand(stringSetting(CdbAdditionalArguments));
552
553
554
    switch (sp.startMode) {
    case StartInternal:
    case StartExternal:
555
556
        if (!nativeArguments.isEmpty())
            nativeArguments.push_back(blank);
557
558
        QtcProcess::addArgs(&nativeArguments,
                            QStringList(QDir::toNativeSeparators(sp.inferior.executable)));
559
        break;
560
    case AttachToRemoteServer:
561
        break;
562
    case AttachExternal:
563
    case AttachCrashedExternal:
hjk's avatar
hjk committed
564
        arguments << "-p" << QString::number(sp.attachPID);
565
        if (sp.startMode == AttachCrashedExternal) {
hjk's avatar
hjk committed
566
            arguments << "-e" << sp.crashParameter << "-g";
567
        } else {
568
            if (isCreatorConsole(runParameters()))
hjk's avatar
hjk committed
569
                arguments << "-pr" << "-pb";
570
        }
571
        break;
572
    case AttachCore:
hjk's avatar
hjk committed
573
        arguments << "-z" << sp.coreFile;
574
        break;
575
    default:
hjk's avatar
hjk committed
576
        *errorMessage = QString("Internal error: Unsupported start mode %1.").arg(sp.startMode);
577
578
        return false;
    }
579
    if (!sp.inferior.commandLineArguments.isEmpty()) { // Complete native argument string.
580
581
        if (!nativeArguments.isEmpty())
            nativeArguments.push_back(blank);
582
        nativeArguments += sp.inferior.commandLineArguments;
583
584
    }

hjk's avatar
hjk committed
585
    const QString msg = QString("Launching %1 %2\nusing %3 of %4.").
586
            arg(QDir::toNativeSeparators(executable),
hjk's avatar
hjk committed
587
                arguments.join(blank) + blank + nativeArguments,
588
589
590
591
592
                QDir::toNativeSeparators(extensionFi.absoluteFilePath()),
                extensionFi.lastModified().toString(Qt::SystemLocaleShortDate));
    showMessage(msg, LogMisc);

    m_outputBuffer.clear();
593
    m_autoBreakPointCorrection = false;
594
    const QStringList inferiorEnvironment = sp.inferior.environment.size() == 0 ?
595
                                    QProcessEnvironment::systemEnvironment().toStringList() :
596
                                    sp.inferior.environment.toStringList();
597
    m_process.setEnvironment(mergeEnvironment(inferiorEnvironment, extensionFi.absolutePath()));
598
599
    if (!sp.inferior.workingDirectory.isEmpty())
        m_process.setWorkingDirectory(sp.inferior.workingDirectory);
600

601
#ifdef Q_OS_WIN
602
603
    if (!nativeArguments.isEmpty()) // Appends
        m_process.setNativeArguments(nativeArguments);
604
#endif
605
606
    m_process.start(executable, arguments);
    if (!m_process.waitForStarted()) {
hjk's avatar
hjk committed
607
        *errorMessage = QString("Internal error: Cannot start process %1: %2").
608
609
610
                arg(QDir::toNativeSeparators(executable), m_process.errorString());
        return false;
    }
hjk's avatar
hjk committed
611

612
    const qint64 pid = m_process.processId();
hjk's avatar
hjk committed
613
    showMessage(QString("%1 running as %2").
614
615
                arg(QDir::toNativeSeparators(executable)).arg(pid), LogMisc);
    m_hasDebuggee = true;
616
617
    if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
        m_accessible = true;
hjk's avatar
hjk committed
618
        runCommand({".load " + extensionFileName, NoFlags});
619
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
620
621
        notifyEngineSetupOk();
    }
622
623
624
625
626
    return true;
}

void CdbEngine::setupInferior()
{
627
628
    if (debug)
        qDebug("setupInferior");
629
630
    const DebuggerRunParameters &rp = runParameters();
    if (!rp.commandsAfterConnect.isEmpty())
631
        runCommand({rp.commandsAfterConnect, NoFlags});
632
633
634
    // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
    // (attemptBreakpointSynchronization() will be directly called then)
    attemptBreakpointSynchronization();
635
    if (rp.breakOnMain) {
636
        const BreakpointParameters bp(BreakpointAtMain);
637
        BreakpointModelId id(quint16(-1));
hjk's avatar
hjk committed
638
639
        QString function = cdbAddBreakpointCommand(bp, m_sourcePathMappings, id, true);
        runCommand({function, BuiltinCommand,
640
641
                    [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }});
    }
642
643
644

    // setting up symbol search path
    QStringList symbolPaths = stringListSetting(CdbSymbolPaths);
645
    const QProcessEnvironment &env = m_process.processEnvironment();
hjk's avatar
hjk committed
646
    QString symbolPath = env.value("_NT_ALT_SYMBOL_PATH");
647
648
    if (!symbolPath.isEmpty())
        symbolPaths += symbolPath;
hjk's avatar
hjk committed
649
    symbolPath = env.value("_NT_SYMBOL_PATH");
650
651
    if (!symbolPath.isEmpty())
        symbolPaths += symbolPath;
hjk's avatar
hjk committed
652
    runCommand({".sympath \"" + symbolPaths.join(';') + '"', NoFlags});
653

654
655
656
    runCommand({"sxn 0x4000001f", NoFlags}); // Do not break on WowX86 exceptions.
    runCommand({"sxn ibp", NoFlags}); // Do not break on initial breakpoints.
    runCommand({".asm source_line", NoFlags}); // Source line in assembly
hjk's avatar
hjk committed
657
658
    runCommand({m_extensionCommandPrefix + "setparameter maxStringLength="
                + action(MaximalStringLength)->value().toString()
659
                + " maxStackDepth="
hjk's avatar
hjk committed
660
                + action(MaximalStackDepth)->value().toString(), NoFlags});
661

662
663
    runCommand({"print(sys.version)", ScriptCommand, CB(setupScripting)});

664
    runCommand({"pid", ExtensionCommand, [this](const DebuggerResponse &response) {
665
666
667
668
669
670
671
        // Fails for core dumps.
        if (response.resultClass == ResultDone)
            notifyInferiorPid(response.data.data().toULongLong());
        if (response.resultClass == ResultDone || runParameters().startMode == AttachCore) {
            STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk")
                    notifyInferiorSetupOk();
        }  else {
hjk's avatar
hjk committed
672
673
            showMessage(QString("Failed to determine inferior pid: %1").
                        arg(response.data["msg"].data()), LogError);
674
675
676
            STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed")
                    notifyInferiorSetupFailed();
        }
677
    }});
678
679
}

hjk's avatar
hjk committed
680
static QString msvcRunTime(const Abi::OSFlavor flavour)
681
682
683
{
    switch (flavour)  {
    case Abi::WindowsMsvc2005Flavor:
hjk's avatar
hjk committed
684
        return QLatin1String("MSVCR80");
685
    case Abi::WindowsMsvc2008Flavor:
hjk's avatar
hjk committed
686
        return QLatin1String("MSVCR90");
687
    case Abi::WindowsMsvc2010Flavor:
hjk's avatar
hjk committed
688
        return QLatin1String("MSVCR100");
689
    case Abi::WindowsMsvc2012Flavor:
hjk's avatar
hjk committed
690
        return QLatin1String("MSVCR110");
691
    case Abi::WindowsMsvc2013Flavor:
hjk's avatar
hjk committed
692
        return QLatin1String("MSVCR120");
Joerg Bornemann's avatar
Joerg Bornemann committed
693
    case Abi::WindowsMsvc2015Flavor:
hjk's avatar
hjk committed
694
        return QLatin1String("MSVCR140");
695
696
697
    default:
        break;
    }
hjk's avatar
hjk committed
698
    return QLatin1String("MSVCRT"); // MinGW, others.
699
700
}

hjk's avatar
hjk committed
701
702
static QString breakAtFunctionCommand(const QString &function,
                                      const QString &module = QString())
703
{
hjk's avatar
hjk committed
704
     QString result = "bu ";
705
706
707
708
709
710
711
712
     if (!module.isEmpty()) {
         result += module;
         result += '!';
     }
     result += function;
     return result;
}

713
714
void CdbEngine::runEngine()
{
715
716
    if (debug)
        qDebug("runEngine");
717

hjk's avatar
hjk committed
718
    const QStringList breakEvents = stringListSetting(CdbBreakEvents);
719
    foreach (const QString &breakEvent, breakEvents)
hjk's avatar
hjk committed
720
        runCommand({"sxe " + breakEvent, NoFlags});
721
722
    // Break functions: each function must be fully qualified,
    // else the debugger will slow down considerably.
723
    const auto cb = [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); };
hjk's avatar
hjk committed
724
    if (boolSetting(CdbBreakOnCrtDbgReport)) {
hjk's avatar
hjk committed
725
726
727
728
        const QString module = msvcRunTime(runParameters().toolChainAbi.osFlavor());
        const QString debugModule = module + 'D';
        const QString wideFunc = QString::fromLatin1(CdbOptionsPage::crtDbgReport).append('W');
        runCommand({breakAtFunctionCommand(QLatin1String(CdbOptionsPage::crtDbgReport), module), BuiltinCommand, cb});
729
        runCommand({breakAtFunctionCommand(wideFunc, module), BuiltinCommand, cb});
hjk's avatar
hjk committed
730
        runCommand({breakAtFunctionCommand(QLatin1String(CdbOptionsPage::crtDbgReport), debugModule), BuiltinCommand, cb});
731
    }
hjk's avatar
hjk committed
732
    if (boolSetting(BreakOnWarning)) {
733
734
        runCommand({"bm /( QtCored4!qWarning", BuiltinCommand}); // 'bm': All overloads.
        runCommand({"bm /( Qt5Cored!QMessageLogger::warning", BuiltinCommand});
735
    }
hjk's avatar
hjk committed
736
    if (boolSetting(BreakOnFatal)) {
737
738
        runCommand({"bm /( QtCored4!qFatal", BuiltinCommand}); // 'bm': All overloads.
        runCommand({"bm /( Qt5Cored!QMessageLogger::fatal", BuiltinCommand});
739
    }
740
    if (runParameters().startMode == AttachCore) {
741
        QTC_ASSERT(!m_coreStopReason.isNull(), return; );
hjk's avatar
hjk committed
742
        notifyEngineRunOkAndInferiorUnrunnable();
743
744
        processStop(*m_coreStopReason, false);
    } else {
745
        doContinueInferior();
746
    }
747
748
}

749
750
bool CdbEngine::commandsPending() const
{
751
    return !m_commandForToken.isEmpty();
752
753
}

754
755
756
void CdbEngine::shutdownInferior()
{
    if (debug)
hjk's avatar
hjk committed
757
758
        qDebug("CdbEngine::shutdownInferior in state '%s', process running %d",
               qPrintable(stateName(state())), isCdbProcessRunning());
759
760
761
762

    if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
        if (debug)
            qDebug("notifyInferiorShutdownOk");
763
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
764
765
766
        notifyInferiorShutdownOk();
        return;
    }
767

768
    if (m_accessible) { // except console.
769
        if (runParameters().startMode == AttachExternal || runParameters().startMode == AttachCrashedExternal)
770
            detachDebugger();
771
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk")
772
773
        notifyInferiorShutdownOk();
    } else {
774
775
        // A command got stuck.
        if (commandsPending()) {
hjk's avatar
hjk committed
776
            showMessage("Cannot shut down inferior due to pending commands.", LogWarning);
777
778
779
780
781
            STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
            notifyInferiorShutdownFailed();
            return;
        }
        if (!canInterruptInferior()) {
hjk's avatar
hjk committed
782
            showMessage("Cannot interrupt the inferior.", LogWarning);
783
784
785
786
            STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed")
            notifyInferiorShutdownFailed();
            return;
        }
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
        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)
802
803
        qDebug("CdbEngine::shutdownEngine in state '%s', process running %d,"
               "accessible=%d,commands pending=%d",
hjk's avatar
hjk committed
804
               qPrintable(stateName(state())), isCdbProcessRunning(), m_accessible,
805
               commandsPending());
806
807

    if (!isCdbProcessRunning()) { // Direct launch: Terminated with process.
808
        STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk")
809
810
811
812
        notifyEngineShutdownOk();
        return;
    }

813
    // No longer trigger anything from messages
814
    m_ignoreCdbOutput = true;
815
816
    // Go for kill if there are commands pending.
    if (m_accessible && !commandsPending()) {
817
        // detach (except console): Wait for debugger to finish.
818
        if (runParameters().startMode == AttachExternal || runParameters().startMode == AttachCrashedExternal)
819
            detachDebugger();
820
        // Remote requires a bit more force to quit.
821
        if (m_effectiveStartMode == AttachToRemoteServer) {