gdbengine.cpp 160 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
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
****************************************************************************/
con's avatar
con committed
25 26

#include "gdbengine.h"
27

28 29
#include "attachgdbadapter.h"
#include "coregdbadapter.h"
30
#include "gdbplainengine.h"
31
#include "termgdbadapter.h"
ck's avatar
ck committed
32
#include "remotegdbserveradapter.h"
con's avatar
con committed
33

34 35
#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggerinternalconstants.h>
36
#include <debugger/debuggerruncontrol.h>
37 38 39 40 41 42 43 44 45 46 47
#include <debugger/disassemblerlines.h>

#include <debugger/debuggeractions.h>
#include <debugger/debuggercore.h>
#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerplugin.h>
#include <debugger/debuggerprotocol.h>
#include <debugger/debuggertooltipmanager.h>
#include <debugger/disassembleragent.h>
#include <debugger/memoryagent.h>
#include <debugger/sourceutils.h>
48
#include <debugger/terminal.h>
49 50 51 52 53 54 55 56 57 58 59

#include <debugger/breakhandler.h>
#include <debugger/moduleshandler.h>
#include <debugger/registerhandler.h>
#include <debugger/sourcefileshandler.h>
#include <debugger/stackhandler.h>
#include <debugger/threadshandler.h>
#include <debugger/debuggersourcepathmappingwidget.h>
#include <debugger/logwindow.h>
#include <debugger/procinterrupt.h>
#include <debugger/shared/hostutils.h>
con's avatar
con committed
60

61
#include <coreplugin/icore.h>
62
#include <coreplugin/messagebox.h>
63

64
#include <projectexplorer/devicesupport/deviceprocess.h>
65
#include <projectexplorer/itaskhandler.h>
66
#include <projectexplorer/projectexplorer.h>
67
#include <projectexplorer/taskhub.h>
hjk's avatar
hjk committed
68

69
#include <utils/algorithm.h>
70
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
71
#include <utils/macroexpander.h>
72 73
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
hjk's avatar
hjk committed
74
#include <utils/savedaction.h>
75
#include <utils/temporaryfile.h>
hjk's avatar
hjk committed
76

hjk's avatar
hjk committed
77
#include <QBuffer>
78
#include <QDirIterator>
79
#include <QMessageBox>
80
#include <QProcess>
81
#include <QPushButton>
82
#include <QJsonArray>
con's avatar
con committed
83

hjk's avatar
hjk committed
84
using namespace Core;
hjk's avatar
hjk committed
85
using namespace ProjectExplorer;
86
using namespace Utils;
hjk's avatar
hjk committed
87

88 89
namespace Debugger {
namespace Internal {
con's avatar
con committed
90

91 92 93
enum { debugPending = 0 };

#define PENDING_DEBUG(s) do { if (debugPending) qDebug() << s; } while (0)
con's avatar
con committed
94

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

97
#define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0)
98

hjk's avatar
hjk committed
99
QString GdbEngine::tooltipIName(const QString &exp)
100
{
hjk's avatar
hjk committed
101
    return "tooltip." + toHex(exp);
102 103
}

hjk's avatar
hjk committed
104
static bool stateAcceptsGdbCommands(DebuggerState state)
hjk's avatar
hjk committed
105
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
106
    switch (state) {
hjk's avatar
hjk committed
107
    case EngineSetupRequested:
108 109
    case EngineSetupOk:
    case EngineSetupFailed:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
110
    case InferiorUnrunnable:
hjk's avatar
hjk committed
111
    case InferiorSetupRequested:
hjk's avatar
hjk committed
112
    case InferiorSetupFailed:
hjk's avatar
hjk committed
113 114 115 116 117 118 119 120
    case EngineRunRequested:
    case InferiorRunRequested:
    case InferiorRunOk:
    case InferiorStopRequested:
    case InferiorStopOk:
    case InferiorShutdownRequested:
    case EngineShutdownRequested:
    case InferiorShutdownOk:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
121 122 123 124
    case InferiorShutdownFailed:
        return true;
    case DebuggerNotReady:
    case InferiorStopFailed:
125
    case InferiorSetupOk:
hjk's avatar
hjk committed
126 127 128 129 130
    case EngineRunFailed:
    case InferiorRunFailed:
    case EngineShutdownOk:
    case EngineShutdownFailed:
    case DebuggerFinished:
131
        return false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
132 133 134
    }
    return false;
}
hjk's avatar
hjk committed
135

con's avatar
con committed
136 137 138 139 140 141
static int &currentToken()
{
    static int token = 0;
    return token;
}

142
static bool isMostlyHarmlessMessage(const QStringRef &msg)
143 144 145
{
    return msg == "warning: GDB: Failed to set controlling terminal: "
                  "Inappropriate ioctl for device\\n"
146 147
        || msg == "warning: GDB: Failed to set controlling terminal: "
                  "Invalid argument\\n";
148 149
}

150 151 152 153 154
static QString mainFunction(const DebuggerRunParameters &rp)
{
    return QLatin1String(rp.toolChainAbi.os() == Abi::WindowsOS && !rp.useTerminal ? "qMain" : "main");
}

155 156 157 158 159 160 161 162 163 164 165 166
///////////////////////////////////////////////////////////////////////
//
// Debuginfo Taskhandler
//
///////////////////////////////////////////////////////////////////////

class DebugInfoTask
{
public:
    QString command;
};

hjk's avatar
hjk committed
167
class DebugInfoTaskHandler : public ITaskHandler
168 169
{
public:
hjk's avatar
hjk committed
170 171
    explicit DebugInfoTaskHandler(GdbEngine *engine)
        : m_engine(engine)
172 173
    {}

174
    bool canHandle(const Task &task) const override
175 176 177 178
    {
        return m_debugInfoTasks.contains(task.taskId);
    }

179
    void handle(const Task &task) override
180 181 182 183 184 185 186 187 188
    {
        m_engine->requestDebugInformation(m_debugInfoTasks.value(task.taskId));
    }

    void addTask(unsigned id, const DebugInfoTask &task)
    {
        m_debugInfoTasks[id] = task;
    }

189
    QAction *createAction(QObject *parent) const override
190
    {
hjk's avatar
hjk committed
191
        QAction *action = new QAction(DebuggerPlugin::tr("Install &Debug Information"), parent);
192
        action->setToolTip(DebuggerPlugin::tr("Tries to install missing debug information."));
193 194 195 196 197 198 199 200
        return action;
    }

private:
    GdbEngine *m_engine;
    QHash<unsigned, DebugInfoTask> m_debugInfoTasks;
};

con's avatar
con committed
201 202 203 204 205 206
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

207
GdbEngine::GdbEngine(bool useTerminal)
con's avatar
con committed
208
{
hjk's avatar
hjk committed
209
    setObjectName("GdbEngine");
210

211
    m_busy = false;
212
    m_gdbVersion = 100;
213
    m_pythonVersion = 0;
214
    m_isQnxGdb = false;
215 216 217
    m_registerNamesListed = false;
    m_sourcesListUpdating = false;
    m_oldestAcceptableToken = -1;
218
    m_nonDiscardableCount = 0;
219 220
    m_gdbOutputCodec = QTextCodec::codecForLocale();
    m_inferiorOutputCodec = QTextCodec::codecForLocale();
221 222
    m_pendingBreakpointRequests = 0;
    m_commandsDoneCallback = 0;
223
    m_stackNeeded = false;
224
    m_terminalTrap = useTerminal;
225
    m_systemDumpersLoaded = false;
226
    m_rerunPending = false;
227
    m_inUpdateLocals = false;
228

229
    m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
hjk's avatar
hjk committed
230
    //ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler);
231

232
    m_commandTimer.setSingleShot(true);
hjk's avatar
hjk committed
233 234 235 236 237 238 239 240 241 242 243
    connect(&m_commandTimer, &QTimer::timeout,
            this, &GdbEngine::commandTimeout);

    connect(action(AutoDerefPointers), &SavedAction::valueChanged,
            this, &GdbEngine::reloadLocals);
    connect(action(CreateFullBacktrace), &QAction::triggered,
            this, &GdbEngine::createFullBacktrace);
    connect(action(UseDebuggingHelpers), &SavedAction::valueChanged,
            this, &GdbEngine::reloadLocals);
    connect(action(UseDynamicType), &SavedAction::valueChanged,
            this, &GdbEngine::reloadLocals);
ck's avatar
ck committed
244 245
}

con's avatar
con committed
246 247
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
248
    //ExtensionSystem::PluginManager::removeObject(m_debugInfoTaskHandler);
249 250 251
    delete m_debugInfoTaskHandler;
    m_debugInfoTaskHandler = 0;

252
    // Prevent sending error messages afterwards.
253 254 255 256 257
    disconnect();
}

DebuggerStartMode GdbEngine::startMode() const
{
258
    return runParameters().startMode;
hjk's avatar
hjk committed
259 260
}

261 262 263 264 265
QString GdbEngine::failedToStartMessage()
{
    return tr("The gdb process failed to start.");
}

con's avatar
con committed
266 267 268
#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
hjk's avatar
hjk committed
269
    QString ba(first, middle - first);
270
    Q_UNUSED(to)
con's avatar
con committed
271 272 273 274 275 276 277 278 279 280 281
    // note that qDebug cuts off output after a certain size... (bug?)
    qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
        qPrintable(currentTime()),
        qPrintable(QString(ba).trimmed()),
        qPrintable(to.trimmed()));
    //qDebug() << "";
    //qDebug() << qPrintable(currentTime())
    //    << " Reading response:  " << QString(ba).trimmed() << "\n";
}
#endif

282 283
// Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
// and return an exception message
hjk's avatar
hjk committed
284
static inline QString msgWinException(const QString &data, unsigned *exCodeIn = 0)
285
{
286 287
    if (exCodeIn)
        *exCodeIn = 0;
288 289 290 291 292 293
    const int exCodePos = data.indexOf("0x");
    const int blankPos = exCodePos != -1 ? data.indexOf(' ', exCodePos + 1) : -1;
    const int addressPos = blankPos != -1 ? data.indexOf("0x", blankPos + 1) : -1;
    if (addressPos < 0)
        return GdbEngine::tr("An exception was triggered.");
    const unsigned exCode = data.mid(exCodePos, blankPos - exCodePos).toUInt(0, 0);
294 295
    if (exCodeIn)
        *exCodeIn = exCode;
296 297 298
    const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
    QString rc;
    QTextStream str(&rc);
299
    str << GdbEngine::tr("An exception was triggered:") << ' ';
300 301 302 303 304
    formatWindowsException(exCode, address, 0, 0, 0, str);
    str << '.';
    return rc;
}

hjk's avatar
hjk committed
305
static bool isNameChar(int c)
hjk's avatar
hjk committed
306 307 308 309 310
{
    // could be 'stopped' or 'shlibs-added'
    return (c >= 'a' && c <= 'z') || c == '-';
}

hjk's avatar
hjk committed
311
static bool contains(const QString &message, const QString &pattern, int size)
312 313 314 315 316 317 318
{
    const int s = message.size();
    if (s < size)
        return false;
    const int pos = message.indexOf(pattern);
    if (pos == -1)
        return false;
319 320 321
    const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
    const bool endFits = pos + size == s || message.at(pos + size) == '\n';
    return beginFits && endFits;
322 323
}

hjk's avatar
hjk committed
324
static bool isGdbConnectionError(const QString &message)
325 326 327 328 329 330 331
{
    // Handle messages gdb client produces when the target exits (gdbserver)
    //
    // we get this as response either to a specific command, e.g.
    //    31^error,msg="Remote connection closed"
    // or as informative output:
    //    &Remote connection closed
332 333 334 335 336 337 338 339

    const char msg1[] = "Remote connection closed";
    const char msg2[] = "Remote communication error.  Target disconnected.: No error.";
    const char msg3[] = "Quit";

    return contains(message, msg1, sizeof(msg1) - 1)
        || contains(message, msg2, sizeof(msg2) - 1)
        || contains(message, msg3, sizeof(msg3) - 1);
340 341
}

hjk's avatar
hjk committed
342
void GdbEngine::handleResponse(const QString &buff)
con's avatar
con committed
343
{
hjk's avatar
hjk committed
344
    showMessage(buff, LogOutput);
con's avatar
con committed
345

346 347
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
348

hjk's avatar
hjk committed
349 350 351
    const QChar *from = buff.constData();
    const QChar *to = from + buff.size();
    const QChar *inner;
con's avatar
con committed
352

353
    int token = -1;
354
    // Token is a sequence of numbers.
355 356
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
357
            break;
358
    if (from != inner) {
hjk's avatar
hjk committed
359
        token = QString(from, inner - from).toInt();
360 361
        from = inner;
    }
con's avatar
con committed
362

363
    // Next char decides kind of response.
hjk's avatar
hjk committed
364 365
    const QChar c = *from++;
    switch (c.unicode()) {
366 367 368
        case '*':
        case '+':
        case '=': {
hjk's avatar
hjk committed
369
            QString asyncClass;
370
            for (; from != to; ++from) {
hjk's avatar
hjk committed
371 372
                const QChar c = *from;
                if (!isNameChar(c.unicode()))
373 374 375
                    break;
                asyncClass += *from;
            }
con's avatar
con committed
376

hjk's avatar
hjk committed
377
            GdbMi result;
378 379
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
380
                if (*from != ',') {
381 382
                    // happens on archer where we get
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb)
hjk's avatar
hjk committed
383
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
384 385 386 387 388
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
389
                    //qDebug() << "parsed result:" << data.toString();
390
                    result.m_children.push_back(data);
hjk's avatar
hjk committed
391
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
392 393
                }
            }
394
            handleAsyncOutput(asyncClass, result);
395 396
            break;
        }
397

398
        case '~': {
hjk's avatar
hjk committed
399
            QString data = GdbMi::parseCString(from, to);
400
            if (data.startsWith("bridgemessage={")) {
401
                // It's already logged.
402 403
                break;
            }
404 405 406
            if (data.startsWith("interpreterresult={")) {
                GdbMi allData;
                allData.fromStringMultiple(data);
407 408
                DebuggerResponse response;
                response.resultClass = ResultDone;
409 410
                response.data = allData["interpreterresult"];
                response.token = allData["token"].toInt();
411 412 413
                handleResultRecord(&response);
                break;
            }
414 415 416
            if (data.startsWith("interpreterasync={")) {
                GdbMi allData;
                allData.fromStringMultiple(data);
hjk's avatar
hjk committed
417
                QString asyncClass = allData["asyncclass"].data();
418 419 420 421
                if (asyncClass == "breakpointmodified")
                    handleInterpreterBreakpointModified(allData["interpreterasync"]);
                break;
            }
422
            m_pendingConsoleStreamOutput += data;
423 424

            // Show some messages to give the impression something happens.
425
            if (data.startsWith("Reading symbols from ")) {
hjk's avatar
hjk committed
426
                showStatusMessage(tr("Reading %1...").arg(data.mid(21)), 1000);
427
                progressPing();
428 429 430
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
431
                progressPing();
hjk's avatar
hjk committed
432
                showStatusMessage(data, 1000);
433 434 435 436
            } else if (data.startsWith("gdb: unknown target exception 0x")) {
                // [Windows, most likely some DLL/Entry point not found]:
                // "gdb: unknown target exception 0xc0000139 at 0x77bef04e"
                // This may be fatal and cause the target to exit later
437 438
                unsigned exCode;
                m_lastWinException = msgWinException(data, &exCode);
439
                showMessage(m_lastWinException, LogMisc);
440
                const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning;
441
                TaskHub::addTask(type, m_lastWinException, Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
442
            }
443 444
            break;
        }
445

446
        case '@': {
hjk's avatar
hjk committed
447
            QString data = GdbMi::parseCString(from, to);
448
            QString msg = data.left(data.size() - 1);
449
            showMessage(msg, AppOutput);
450
            break;
con's avatar
con committed
451 452
        }

453
        case '&': {
hjk's avatar
hjk committed
454
            QString data = GdbMi::parseCString(from, to);
455 456
            // On Windows, the contents seem to depend on the debugger
            // version and/or OS version used.
457
            if (data.startsWith("warning:"))
hjk's avatar
hjk committed
458
                showMessage(data.mid(9), AppStuff); // Cut "warning: "
459 460

            m_pendingLogStreamOutput += data;
461

462
            if (isGdbConnectionError(data)) {
463
                notifyInferiorExited();
464
                break;
465
            }
466

hjk's avatar
hjk committed
467
            if (boolSetting(IdentifyDebugInfoPackages)) {
468 469 470
                // From SuSE's gdb: >&"Missing separate debuginfo for ...\n"
                // ">&"Try: zypper install -C \"debuginfo(build-id)=c084ee5876ed1ac12730181c9f07c3e027d8e943\"\n"
                if (data.startsWith("Missing separate debuginfo for ")) {
hjk's avatar
hjk committed
471
                    m_lastMissingDebugInfo = data.mid(32);
472
                } else if (data.startsWith("Try: zypper")) {
hjk's avatar
hjk committed
473
                    QString cmd = data.mid(4);
474 475 476 477

                    Task task(Task::Warning,
                        tr("Missing debug information for %1\nTry: %2")
                            .arg(m_lastMissingDebugInfo).arg(cmd),
hjk's avatar
hjk committed
478
                        FileName(), 0, Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO);
479

Nicolas Arnaud-Cormos's avatar
Nicolas Arnaud-Cormos committed
480
                    TaskHub::addTask(task);
481 482 483 484 485

                    DebugInfoTask dit;
                    dit.command = cmd;
                    m_debugInfoTaskHandler->addTask(task.taskId, dit);
                }
486 487
            }

con's avatar
con committed
488 489 490
            break;
        }

491
        case '^': {
492
            DebuggerResponse response;
con's avatar
con committed
493

hjk's avatar
hjk committed
494
            response.token = token;
con's avatar
con committed
495

496 497 498
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
499

hjk's avatar
hjk committed
500
            QString resultClass = QString::fromRawData(from, inner - from);
501
            if (resultClass == "done")
502
                response.resultClass = ResultDone;
503
            else if (resultClass == "running")
504
                response.resultClass = ResultRunning;
505
            else if (resultClass == "connected")
506
                response.resultClass = ResultConnected;
507
            else if (resultClass == "error")
508
                response.resultClass = ResultError;
509
            else if (resultClass == "exit")
510
                response.resultClass = ResultExit;
511
            else
512
                response.resultClass = ResultUnknown;
con's avatar
con committed
513

514 515
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
516 517
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
518 519 520
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
521
                } else {
522
                    // Archer has this.
hjk's avatar
hjk committed
523 524
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
525 526
                }
            }
527

528 529
            response.logStreamOutput = m_pendingLogStreamOutput;
            response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
530 531 532
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

533 534 535
            if (response.data.data().isEmpty())
                response.data.fromString(response.consoleStreamOutput);

536
            handleResultRecord(&response);
537 538 539
            break;
        }
        default: {
540
            qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
541
            break;
con's avatar
con committed
542 543 544 545
        }
    }
}

hjk's avatar
hjk committed
546
void GdbEngine::handleAsyncOutput(const QString &asyncClass, const GdbMi &result)
547 548 549
{
    if (asyncClass == "stopped") {
        if (m_inUpdateLocals) {
hjk's avatar
hjk committed
550
            showMessage("UNEXPECTED *stopped NOTIFICATION IGNORED", LogWarning);
551 552 553 554 555 556 557
        } else {
            handleStopResponse(result);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();
        }
    } else if (asyncClass == "running") {
        if (m_inUpdateLocals) {
hjk's avatar
hjk committed
558
            showMessage("UNEXPECTED *running NOTIFICATION IGNORED", LogWarning);
559 560 561
        } else {
            GdbMi threads = result["thread-id"];
            threadsHandler()->notifyRunning(threads.data());
562 563 564 565
            if (runParameters().toolChainAbi.os() == Abi::WindowsOS) {
                // NOTE: Each created thread spits out a *running message. We completely ignore them
                // on Windows, and handle only numbered responses

566 567 568
                // FIXME: Breakpoints on Windows are exceptions which are thrown in newly
                // created threads so we have to filter out the running threads messages when
                // we request a stop.
569 570
            } else if (state() == InferiorRunOk || state() == InferiorSetupRequested) {
                // We get multiple *running after thread creation and in Windows terminals.
hjk's avatar
hjk committed
571 572
                showMessage(QString("NOTE: INFERIOR STILL RUNNING IN STATE %1.").
                            arg(DebuggerEngine::stateName(state())));
573 574 575 576 577 578 579 580 581 582 583 584 585 586
            } else {
                notifyInferiorRunOk();
            }
        }
    } else if (asyncClass == "library-loaded") {
        // Archer has 'id="/usr/lib/libdrm.so.2",
        // target-name="/usr/lib/libdrm.so.2",
        // host-name="/usr/lib/libdrm.so.2",
        // symbols-loaded="0"

        // id="/lib/i386-linux-gnu/libc.so.6"
        // target-name="/lib/i386-linux-gnu/libc.so.6"
        // host-name="/lib/i386-linux-gnu/libc.so.6"
        // symbols-loaded="0",thread-group="i1"
hjk's avatar
hjk committed
587
        QString id = result["id"].data();
588
        if (!id.isEmpty())
hjk's avatar
hjk committed
589
            showStatusMessage(tr("Library %1 loaded").arg(id), 1000);
590 591 592 593
        progressPing();
        Module module;
        module.startAddress = 0;
        module.endAddress = 0;
hjk's avatar
hjk committed
594 595
        module.hostPath = result["host-name"].data();
        module.modulePath = result["target-name"].data();
596 597 598 599 600 601
        module.moduleName = QFileInfo(module.hostPath).baseName();
        modulesHandler()->updateModule(module);
    } else if (asyncClass == "library-unloaded") {
        // Archer has 'id="/usr/lib/libdrm.so.2",
        // target-name="/usr/lib/libdrm.so.2",
        // host-name="/usr/lib/libdrm.so.2"
hjk's avatar
hjk committed
602
        QString id = result["id"].data();
603
        progressPing();
hjk's avatar
hjk committed
604
        showStatusMessage(tr("Library %1 unloaded").arg(id), 1000);
605 606
    } else if (asyncClass == "thread-group-added") {
        // 7.1-symbianelf has "{id="i1"}"
607
    } else if (asyncClass == "thread-group-started") {
608 609 610 611 612
        // Archer had only "{id="28902"}" at some point of 6.8.x.
        // *-started seems to be standard in 7.1, but in early
        // 7.0.x, there was a *-created instead.
        progressPing();
        // 7.1.50 has thread-group-started,id="i1",pid="3529"
hjk's avatar
hjk committed
613 614
        QString id = result["id"].data();
        showStatusMessage(tr("Thread group %1 created").arg(id), 1000);
615
        notifyInferiorPid(result["pid"].toProcessHandle());
616 617 618
        handleThreadGroupCreated(result);
    } else if (asyncClass == "thread-created") {
        //"{id="1",group-id="28902"}"
hjk's avatar
hjk committed
619 620
        QString id = result["id"].data();
        showStatusMessage(tr("Thread %1 created").arg(id), 1000);
621 622 623 624 625 626
        ThreadData thread;
        thread.id = ThreadId(id.toLong());
        thread.groupId = result["group-id"].data();
        threadsHandler()->updateThread(thread);
    } else if (asyncClass == "thread-group-exited") {
        // Archer has "{id="28902"}"
hjk's avatar
hjk committed
627 628
        QString id = result["id"].data();
        showStatusMessage(tr("Thread group %1 exited").arg(id), 1000);
629 630 631
        handleThreadGroupExited(result);
    } else if (asyncClass == "thread-exited") {
        //"{id="1",group-id="28902"}"
hjk's avatar
hjk committed
632 633
        QString id = result["id"].data();
        QString groupid = result["group-id"].data();
634
        showStatusMessage(tr("Thread %1 in group %2 exited")
hjk's avatar
hjk committed
635
                          .arg(id).arg(groupid), 1000);
636 637
        threadsHandler()->removeThread(ThreadId(id.toLong()));
    } else if (asyncClass == "thread-selected") {
hjk's avatar
hjk committed
638 639
        QString id = result["id"].data();
        showStatusMessage(tr("Thread %1 selected").arg(id), 1000);
640 641 642 643 644 645 646 647 648 649 650 651 652
        //"{id="2"}"
    } else if (asyncClass == "breakpoint-modified") {
        // New in FSF gdb since 2011-04-27.
        // "{bkpt={number="3",type="breakpoint",disp="keep",
        // enabled="y",addr="<MULTIPLE>",times="1",
        // original-location="\\",simple_gdbtest_app.cpp\\":135"},
        // {number="3.1",enabled="y",addr="0x0805ff68",
        // func="Vector<int>::Vector(int)",
        // file="simple_gdbtest_app.cpp",
        // fullname="/data/...line="135"},{number="3.2"...}}.."

        // Note the leading comma in original-location. Filter it out.
        // We don't need the field anyway.
hjk's avatar
hjk committed
653
        QString ba = result.toString();
654 655 656 657 658 659 660 661 662 663 664
        ba = '[' + ba.mid(6, ba.size() - 7) + ']';
        const int pos1 = ba.indexOf(",original-location");
        const int pos2 = ba.indexOf("\":", pos1 + 2);
        const int pos3 = ba.indexOf('"', pos2 + 2);
        ba.remove(pos1, pos3 - pos1 + 1);
        GdbMi res;
        res.fromString(ba);
        BreakHandler *handler = breakHandler();
        Breakpoint bp;
        BreakpointResponse br;
        foreach (const GdbMi &bkpt, res.children()) {
hjk's avatar
hjk committed
665
            const QString nr = bkpt["number"].data();
666
            BreakpointResponseId rid(nr);
667 668 669 670 671 672 673 674 675 676 677 678 679
            if (nr.contains('.')) {
                // A sub-breakpoint.
                BreakpointResponse sub;
                updateResponse(sub, bkpt);
                sub.id = rid;
                sub.type = br.type;
                bp.insertSubBreakpoint(sub);
            } else {
                // A primary breakpoint.
                bp = handler->findBreakpointByResponseId(rid);
                br = bp.response();
                updateResponse(br, bkpt);
                bp.setResponse(br);
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
            }
        }
    } else if (asyncClass == "breakpoint-created") {
        // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
        //  addr="<PENDING>",pending="main",times="0",
        //  original-location="main"}}" -- or --
        // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
        // what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
        BreakHandler *handler = breakHandler();
        foreach (const GdbMi &bkpt, result.children()) {
            BreakpointResponse br;
            br.type = BreakpointByFileAndLine;
            updateResponse(br, bkpt);
            handler->handleAlienBreakpoint(br, this);
        }
    } else if (asyncClass == "breakpoint-deleted") {
        // "breakpoint-deleted" "{id="1"}"
        // New in FSF gdb since 2011-04-27.
hjk's avatar
hjk committed
698
        QString nr = result["id"].data();
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
        BreakpointResponseId rid(nr);
        if (Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid)) {
            // This also triggers when a temporary breakpoint is hit.
            // We do not really want that, as this loses all information.
            // FIXME: Use a special marker for this case?
            // if (!bp.isOneShot()) ... is not sufficient.
            // It keeps temporary "Jump" breakpoints alive.
            bp.removeAlienBreakpoint();
        }
    } else if (asyncClass == "cmd-param-changed") {
        // New since 2012-08-09
        //  "{param="debug remote",value="1"}"
    } else if (asyncClass == "memory-changed") {
        // New since 2013
        //   "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
    } else if (asyncClass == "tsv-created") {
        // New since 2013-02-06
    } else if (asyncClass == "tsv-modified") {
        // New since 2013-02-06
    } else {
        qDebug() << "IGNORED ASYNC OUTPUT"
                 << asyncClass << result.toString();
    }
}

con's avatar
con committed
724 725
void GdbEngine::readGdbStandardError()
{
hjk's avatar
hjk committed
726 727
    QString err = QString::fromUtf8(m_gdbProc.readAllStandardError());
    showMessage("UNEXPECTED GDB STDERR: " + err);
728 729
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
730 731
    if (err.startsWith("BFD: reopening"))
        return;
Jarek Kobus's avatar
Jarek Kobus committed
732
    qWarning() << "Unexpected GDB stderr:" << err;
con's avatar
con committed
733 734
}

hjk's avatar
hjk committed
735 736
void GdbEngine::readDebuggeeOutput(const QByteArray &ba)
{
737 738 739 740 741 742 743
    const QString msg = m_inferiorOutputCodec->toUnicode(ba.constData(), ba.size(),
                                                         &m_inferiorOutputCodecState);

    if (msg.startsWith("&\"") && isMostlyHarmlessMessage(msg.midRef(2, msg.size() - 4)))
        showMessage("Mostly harmless terminal warning suppressed.", LogWarning);
    else
        showMessage(msg, AppStuff);
hjk's avatar
hjk committed
744 745
}

con's avatar
con committed
746 747
void GdbEngine::readGdbStandardOutput()
{
748
    m_commandTimer.start(); // Restart timer.
749

750 751
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
752

753
    QByteArray out = m_gdbProc.readAllStandardOutput();
hjk's avatar
hjk committed
754
    m_inbuffer.append(out);
con's avatar
con committed
755

756
    // This can trigger when a dialog starts a nested event loop.
757 758 759
    if (m_busy)
        return;

760 761
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
762
        int end = m_inbuffer.indexOf('\n', scan);
763 764 765 766 767
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
768
        scan = newstart;
769 770 771 772 773 774 775
        if (end == start)
            continue;
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
776
        m_busy = true;
hjk's avatar
hjk committed
777

778 779
        QString msg = m_gdbOutputCodec->toUnicode(m_inbuffer.constData() + start, end - start,
                                                  &m_gdbOutputCodecState);
hjk's avatar
hjk committed
780 781

        handleResponse(msg);
782
        m_busy = false;
con's avatar
con committed
783
    }
784
    m_inbuffer.clear();
con's avatar
con committed
785 786 787 788
}

void GdbEngine::interruptInferior()
{
789
    CHECK_STATE(InferiorStopRequested);
790

791 792 793
    if (terminal()->sendInterrupt())
        return;

794
    if (usesExecInterrupt()) {
795
        runCommand({"-exec-interrupt"});
796 797
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
hjk's avatar
hjk committed
798
        showMessage("TRYING TO INTERRUPT INFERIOR");
hjk's avatar
hjk committed
799
        if (HostOsInfo::isWindowsHost() && !m_isQnxGdb) {
800 801
            QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); notifyInferiorStopFailed());
            QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed());
802
            m_signalOperation = runTool()->device()->signalOperation();
803
            QTC_ASSERT(m_signalOperation, notifyInferiorStopFailed());
hjk's avatar
hjk committed
804 805
            connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
                    this, &GdbEngine::handleInterruptDeviceInferior);
806

807
            m_signalOperation->setDebuggerCommand(runParameters().debugger.executable);
808 809 810 811 812 813 814 815 816 817
            m_signalOperation->interruptProcess(inferiorPid());
        } else {
            interruptInferior2();
        }
    }
}

void GdbEngine::handleInterruptDeviceInferior(const QString &error)
{
    if (error.isEmpty()) {
hjk's avatar
hjk committed
818
        showMessage("Interrupted " + QString::number(inferiorPid()));
819 820 821 822
        notifyInferiorStopOk();
    } else {
        showMessage(error, LogError);
        notifyInferiorStopFailed();
823
    }
824 825
    m_signalOperation->disconnect(this);
    m_signalOperation.clear();
con's avatar
con committed
826 827
}

828
void GdbEngine::runCommand(const DebuggerCommand &command)
hjk's avatar
hjk committed
829
{
830 831
    const int token = ++currentToken();

832
    DebuggerCommand cmd = command;
hjk's avatar
hjk committed
833

834 835 836 837 838 839
    if (cmd.function.isEmpty()) {
        showMessage(QString("EMPTY FUNCTION RUN, TOKEN: %1 ARGS: %2")
                        .arg(token).arg(cmd.args.toString()));
        QTC_ASSERT(false, return);
    }

hjk's avatar
hjk committed
840
    if (!stateAcceptsGdbCommands(state())) {
hjk's avatar
hjk committed
841 842
        showMessage(QString("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
            .arg(cmd.function).arg(state()));
con's avatar
con committed
843 844 845
        return;
    }

hjk's avatar
hjk committed
846
    if (cmd.flags & RebuildBreakpointModel) {
847
        ++m_pendingBreakpointRequests;
Andre Hartmann's avatar
Andre Hartmann committed
848
        PENDING_DEBUG("   BREAKPOINT MODEL:" << cmd.function
849
                      << "INCREMENTS PENDING TO" << m_pendingBreakpointRequests);
con's avatar
con committed
850
    } else {
851
        PENDING_DEBUG("   OTHER (IN):" << cmd.function
852
                      << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
con's avatar
con committed
853 854
    }

hjk's avatar
hjk committed
855
    if (cmd.flags & (NeedsTemporaryStop|NeedsFullStop)) {
hjk's avatar
hjk committed
856
        showMessage("RUNNING NEEDS-STOP COMMAND " + cmd.function);
hjk's avatar
hjk committed
857 858
        const bool wantContinue = bool(cmd.flags & NeedsTemporaryStop);
        cmd.flags &= ~(NeedsTemporaryStop|NeedsFullStop);
859
        if (state() == InferiorStopRequested) {
hjk's avatar
hjk committed
860
            if (cmd.flags & LosesChild) {
861 862
                notifyInferiorIll();
                return;
863
            }
hjk's avatar
hjk committed
864
            showMessage("CHILD ALREADY BEING INTERRUPTED. STILL HOPING.");
865 866
            // Calling shutdown() here breaks all situations where two
            // NeedsStop commands are issued in quick succession.
hjk's avatar
hjk committed
867 868 869 870
            m_onStop.append(cmd, wantContinue);
            return;
        }
        if (state() == InferiorRunOk) {
871
            showStatusMessage(tr("Stopping temporarily"), 1000);
hjk's avatar
hjk committed
872
            m_onStop.append(cmd, wantContinue);
873
            requestInterruptInferior();
hjk's avatar
hjk committed
874
            return;
hjk's avatar
hjk committed
875
        }
hjk's avatar
hjk committed
876 877 878 879 880 881
        showMessage("UNSAFE STATE FOR QUEUED COMMAND. EXECUTING IMMEDIATELY");
    }

    if (!(cmd.flags & Discardable))
        ++m_nonDiscardableCount;

882
    bool isPythonCommand = true;
883
    if ((cmd.flags & NativeCommand) || cmd.function.contains('-') || cmd.function.contains(' '))
884 885
        isPythonCommand = false;
    if (isPythonCommand) {
hjk's avatar
hjk committed
886 887
        cmd.arg("token", token);
        cmd.function = "python theDumper." + cmd.function + "(" + cmd.argsToPython() + ")";
con's avatar
con committed
888 889
    }

890
    QTC_ASSERT(m_gdbProc.state() == QProcess::Running, return);
891

892
    cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
893
    m_commandForToken[token] = cmd;
hjk's avatar
hjk committed
894 895
    m_flagsForToken[token] = cmd.flags;
    if (cmd.flags & ConsoleCommand)
896
        cmd.function = "-interpreter-exec console \"" + cmd.function + '"';
hjk's avatar
hjk committed
897 898
    cmd.function = QString::number(token) + cmd.function;
    showMessage(cmd.function, LogInput);
899

900 901
    if (m_scheduledTestResponses.contains(token)) {
        // Fake response for test cases.
hjk's avatar
hjk committed
902 903
        QString buffer = m_scheduledTestResponses.value(token);
        buffer.replace("@TOKEN@", QString::number(token));
904
        m_scheduledTestResponses.remove(token);
hjk's avatar
hjk committed
905