gdbengine.cpp 200 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
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 12 13 14
** 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
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
con's avatar
con committed
29 30

#include "gdbengine.h"
31

32 33
#include "attachgdbadapter.h"
#include "coregdbadapter.h"
34
#include "gdbplainengine.h"
35
#include "termgdbadapter.h"
ck's avatar
ck committed
36
#include "remotegdbserveradapter.h"
con's avatar
con committed
37

38
#include "gdboptionspage.h"
con's avatar
con committed
39

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggerinternalconstants.h>
#include <debugger/debuggerruncontrolfactory.h>
#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/debuggerstringutils.h>
#include <debugger/debuggertooltipmanager.h>
#include <debugger/disassembleragent.h>
#include <debugger/memoryagent.h>
#include <debugger/sourceutils.h>

#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
66

67
#include <coreplugin/icore.h>
68 69
#include <projectexplorer/taskhub.h>
#include <projectexplorer/itaskhandler.h>
70
#include <texteditor/itexteditor.h>
71
#include <utils/hostosinfo.h>
72 73
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
hjk's avatar
hjk committed
74
#include <utils/savedaction.h>
hjk's avatar
hjk committed
75

76
#include <QDirIterator>
77
#include <QTemporaryFile>
con's avatar
con committed
78

79 80
#include <QMessageBox>
#include <QPushButton>
con's avatar
con committed
81

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
82
#ifdef Q_OS_UNIX
con's avatar
con committed
83 84 85
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
86
#include <ctype.h>
con's avatar
con committed
87

hjk's avatar
hjk committed
88
using namespace ProjectExplorer;
89
using namespace Utils;
hjk's avatar
hjk committed
90

91 92
namespace Debugger {
namespace Internal {
con's avatar
con committed
93

94 95 96
enum { debugPending = 0 };

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

98 99
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

100
QByteArray GdbEngine::tooltipIName(const QString &exp)
101
{
102
    return "tooltip." + exp.toLatin1().toHex();
103 104
}

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

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

hjk's avatar
hjk committed
144
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
145
{
146
    QByteArray out = response.consoleStreamOutput;
147 148 149 150 151 152 153 154 155
    // FIXME: proper decoding needed
    if (out.endsWith("\\n"))
        out.chop(2);
    while (out.endsWith('\n') || out.endsWith(' '))
        out.chop(1);
    int pos = out.indexOf(" = ");
    return out.mid(pos + 3);
}

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
///////////////////////////////////////////////////////////////////////
//
// Debuginfo Taskhandler
//
///////////////////////////////////////////////////////////////////////

class DebugInfoTask
{
public:
    QString command;
};

class DebugInfoTaskHandler : public  ProjectExplorer::ITaskHandler
{
public:
hjk's avatar
hjk committed
171 172
    explicit DebugInfoTaskHandler(GdbEngine *engine)
        : m_engine(engine)
173 174
    {}

hjk's avatar
hjk committed
175
    bool canHandle(const Task &task) const
176 177 178 179 180 181 182 183 184 185 186 187 188 189
    {
        return m_debugInfoTasks.contains(task.taskId);
    }

    void handle(const Task &task)
    {
        m_engine->requestDebugInformation(m_debugInfoTasks.value(task.taskId));
    }

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

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

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

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

208 209
GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters)
  : DebuggerEngine(startParameters)
con's avatar
con committed
210
{
211
    setObjectName(_("GdbEngine"));
212

213
    m_busy = false;
214 215 216 217
    m_debuggingHelperState = DebuggingHelperUninitialized;
    m_gdbVersion = 100;
    m_gdbBuildVersion = -1;
    m_isMacGdb = false;
218
    m_isQnxGdb = false;
219
    m_hasBreakpointNotifications = false;
220 221 222 223
    m_hasPython = false;
    m_registerNamesListed = false;
    m_sourcesListUpdating = false;
    m_oldestAcceptableToken = -1;
224
    m_nonDiscardableCount = 0;
225 226 227
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingBreakpointRequests = 0;
    m_commandsDoneCallback = 0;
228
    m_stackNeeded = false;
229
    m_preparedForQmlBreak = false;
230
    m_disassembleUsesComma = false;
231
    m_terminalTrap = startParameters.useTerminal;
232
    m_fullStartDone = false;
233
    m_systemDumpersLoaded = false;
hjk's avatar
hjk committed
234
    m_forceAsyncModel = false;
235
    m_pythonAttemptedToLoad = false;
236
    m_gdbProc = new GdbProcess(this);
237

238 239
    invalidateSourcesList();

240
    m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
hjk's avatar
hjk committed
241
    //ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler);
242

243 244 245
    m_commandTimer.setSingleShot(true);
    connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));

hjk's avatar
hjk committed
246
    connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
247
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
248
    connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
249
            SLOT(createFullBacktrace()));
250 251 252 253
    connect(debuggerCore()->action(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
    connect(debuggerCore()->action(UseDynamicType), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
254 255
    connect(debuggerCore()->action(IntelFlavor), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadDisassembly()));
ck's avatar
ck committed
256 257
}

con's avatar
con committed
258 259
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
260
    //ExtensionSystem::PluginManager::removeObject(m_debugInfoTaskHandler);
261 262 263
    delete m_debugInfoTaskHandler;
    m_debugInfoTaskHandler = 0;

264
    // Prevent sending error messages afterwards.
265 266 267 268 269 270
    disconnect();
}

DebuggerStartMode GdbEngine::startMode() const
{
    return startParameters().startMode;
hjk's avatar
hjk committed
271 272
}

273
QString GdbEngine::errorMessage(QProcess::ProcessError error)
con's avatar
con committed
274 275 276
{
    switch (error) {
        case QProcess::FailedToStart:
Jarek Kobus's avatar
Jarek Kobus committed
277
            return tr("The gdb process failed to start. Either the "
278
                "invoked program \"%1\" is missing, or you may have insufficient "
279 280
                "permissions to invoke the program.\n%2")
                .arg(m_gdb, gdbProc()->errorString());
con's avatar
con committed
281
        case QProcess::Crashed:
282 283 284 285 286
            if (targetState() == DebuggerFinished)
                return tr("The gdb process crashed some time after starting "
                    "successfully.");
            else
                return tr("The gdb process was ended forcefully");
con's avatar
con committed
287
        case QProcess::Timedout:
288
            return tr("The last waitFor...() function timed out. "
con's avatar
con committed
289 290 291
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
        case QProcess::WriteError:
292
            return tr("An error occurred when attempting to write "
Jarek Kobus's avatar
Jarek Kobus committed
293
                "to the gdb process. For example, the process may not be running, "
con's avatar
con committed
294 295
                "or it may have closed its input channel.");
        case QProcess::ReadError:
296
            return tr("An error occurred when attempting to read from "
Jarek Kobus's avatar
Jarek Kobus committed
297
                "the gdb process. For example, the process may not be running.");
con's avatar
con committed
298
        default:
Jarek Kobus's avatar
Jarek Kobus committed
299
            return tr("An unknown error in the gdb process occurred. ");
con's avatar
con committed
300 301 302
    }
}

303 304 305 306 307
GdbProcess *GdbEngine::gdbProc() const
{
    return m_gdbProc;
}

con's avatar
con committed
308 309 310 311
#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
312
    Q_UNUSED(to)
con's avatar
con committed
313 314 315 316 317 318 319 320 321 322 323
    // 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

324 325
// Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
// and return an exception message
326
static inline QString msgWinException(const QByteArray &data, unsigned *exCodeIn = 0)
327
{
328 329
    if (exCodeIn)
        *exCodeIn = 0;
330 331 332 333 334 335
    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);
336 337
    if (exCodeIn)
        *exCodeIn = exCode;
338 339 340 341 342 343 344 345 346
    const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
    QString rc;
    QTextStream str(&rc);
    str << GdbEngine::tr("An exception was triggered: ");
    formatWindowsException(exCode, address, 0, 0, 0, str);
    str << '.';
    return rc;
}

347 348
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
349 350
    QString msg = m_outputCodec->toUnicode(data.constData(), data.length(),
        &m_outputCodecState);
351
    showMessage(msg, AppOutput);
352 353
}

hjk's avatar
hjk committed
354 355 356 357 358 359
static bool isNameChar(char c)
{
    // could be 'stopped' or 'shlibs-added'
    return (c >= 'a' && c <= 'z') || c == '-';
}

360 361 362 363 364 365 366 367
static bool contains(const QByteArray &message, const char *pattern, int size)
{
    const int s = message.size();
    if (s < size)
        return false;
    const int pos = message.indexOf(pattern);
    if (pos == -1)
        return false;
368 369 370
    const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
    const bool endFits = pos + size == s || message.at(pos + size) == '\n';
    return beginFits && endFits;
371 372
}

373 374 375 376 377 378 379 380
static bool isGdbConnectionError(const QByteArray &message)
{
    // 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
381 382 383 384 385 386 387 388

    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);
389 390
}

391
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
392
{
393
    showMessage(QString::fromLocal8Bit(buff, buff.length()), LogOutput);
con's avatar
con committed
394

395 396
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
397

398 399 400
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
401

402
    int token = -1;
403
    // Token is a sequence of numbers.
404 405
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
406
            break;
407 408 409 410
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
    }
con's avatar
con committed
411

412
    // Next char decides kind of response.
413 414 415 416 417 418 419 420 421 422 423 424
    const char c = *from++;
    switch (c) {
        case '*':
        case '+':
        case '=': {
            QByteArray asyncClass;
            for (; from != to; ++from) {
                const char c = *from;
                if (!isNameChar(c))
                    break;
                asyncClass += *from;
            }
con's avatar
con committed
425

hjk's avatar
hjk committed
426
            GdbMi result;
427 428
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
429
                if (*from != ',') {
430 431
                    // happens on archer where we get
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb)
hjk's avatar
hjk committed
432
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
433 434 435 436 437
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
438 439 440
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
441 442
                }
            }
443
            if (asyncClass == "stopped") {
444
                handleStopResponse(result);
445 446
                m_pendingLogStreamOutput.clear();
                m_pendingConsoleStreamOutput.clear();
447
            } else if (asyncClass == "running") {
448
                GdbMi threads = result["thread-id"];
hjk's avatar
hjk committed
449
                threadsHandler()->notifyRunning(threads.data());
450 451 452 453
                if (state() == InferiorRunOk || state() == InferiorSetupRequested) {
                    // We get multiple *running after thread creation and in Windows terminals.
                    showMessage(QString::fromLatin1("NOTE: INFERIOR STILL RUNNING IN STATE %1.").
                                arg(QLatin1String(DebuggerEngine::stateName(state()))));
454 455 456
                } else {
                    notifyInferiorRunOk();
                }
457 458 459 460
            } 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",
461
                // symbols-loaded="0"
hjk's avatar
hjk committed
462 463 464 465 466

                // 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"
467
                QByteArray id = result["id"].data();
468
                if (!id.isEmpty())
469
                    showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
470
                progressPing();
471
                invalidateSourcesList();
hjk's avatar
hjk committed
472 473 474
                Module module;
                module.startAddress = 0;
                module.endAddress = 0;
475 476
                module.hostPath = _(result["host-name"].data());
                module.modulePath = _(result["target-name"].data());
hjk's avatar
hjk committed
477
                module.moduleName = QFileInfo(module.hostPath).baseName();
478
                modulesHandler()->updateModule(module);
hjk's avatar
hjk committed
479 480 481 482
            } 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"
483
                QByteArray id = result["id"].data();
484
                progressPing();
485
                showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
486
                invalidateSourcesList();
487 488
            } else if (asyncClass == "thread-group-added") {
                // 7.1-symbianelf has "{id="i1"}"
489 490 491
            } else if (asyncClass == "thread-group-created"
                    || asyncClass == "thread-group-started") {
                // Archer had only "{id="28902"}" at some point of 6.8.x.
hjk's avatar
hjk committed
492 493
                // *-started seems to be standard in 7.1, but in early
                // 7.0.x, there was a *-created instead.
494
                progressPing();
ck's avatar
ck committed
495
                // 7.1.50 has thread-group-started,id="i1",pid="3529"
496
                QByteArray id = result["id"].data();
497
                showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
ck's avatar
ck committed
498 499
                int pid = id.toInt();
                if (!pid) {
500
                    id = result["pid"].data();
ck's avatar
ck committed
501 502 503 504
                    pid = id.toInt();
                }
                if (pid)
                    notifyInferiorPid(pid);
505
                handleThreadGroupCreated(result);
506
            } else if (asyncClass == "thread-created") {
507
                //"{id="1",group-id="28902"}"
508
                QByteArray id = result["id"].data();
509
                showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
hjk's avatar
hjk committed
510 511
                ThreadData thread;
                thread.id = ThreadId(id.toLong());
512
                thread.groupId = result["group-id"].data();
hjk's avatar
hjk committed
513
                threadsHandler()->updateThread(thread);
514
            } else if (asyncClass == "thread-group-exited") {
515
                // Archer has "{id="28902"}"
516
                QByteArray id = result["id"].data();
517
                showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
518
                handleThreadGroupExited(result);
519
            } else if (asyncClass == "thread-exited") {
520
                //"{id="1",group-id="28902"}"
521 522
                QByteArray id = result["id"].data();
                QByteArray groupid = result["group-id"].data();
523
                showStatusMessage(tr("Thread %1 in group %2 exited")
hjk's avatar
hjk committed
524
                    .arg(_(id)).arg(_(groupid)), 1000);
hjk's avatar
hjk committed
525
                threadsHandler()->removeThread(ThreadId(id.toLong()));
hjk's avatar
hjk committed
526
            } else if (asyncClass == "thread-selected") {
527
                QByteArray id = result["id"].data();
528
                showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
529
                //"{id="2"}"
530 531
            } else if (m_isMacGdb && asyncClass == "shlibs-updated") {
                // Apple's gdb announces updated libs.
532
                invalidateSourcesList();
533 534
            } else if (m_isMacGdb && asyncClass == "shlibs-added") {
                // Apple's gdb announces added libs.
535 536 537 538 539
                // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
                // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
                // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
                // description="/usr/lib/system/libmathCommon.A_debug.dylib",
                // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
540
                invalidateSourcesList();
541 542 543 544 545
            } else if (m_isMacGdb && asyncClass == "resolve-pending-breakpoint") {
                // Apple's gdb announces resolved breakpoints.
                // new_bp="1",pended_bp="1",new_expr="\"gdbengine.cpp\":1584",
                // bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
                // addr="0x0000000115cc3ddf",func="foo()",file="../foo.cpp",
546
                // line="1584",shlib="/../libFoo_debug.dylib",times="0"}
547 548
                const GdbMi bkpt = result["bkpt"];
                const BreakpointResponseId rid(bkpt["number"].data());
549
                if (!isQmlStepBreakpoint(rid)) {
hjk's avatar
hjk committed
550
                    BreakHandler *handler = breakHandler();
551
                    BreakpointModelId id = handler->findBreakpointByResponseId(rid);
hjk's avatar
hjk committed
552 553 554
                    BreakpointResponse br = handler->response(id);
                    updateResponse(br, bkpt);
                    handler->setResponse(id, br);
555
                    attemptAdjustBreakpointLocation(id);
556
                }
557 558
            } else if (asyncClass == "breakpoint-modified") {
                // New in FSF gdb since 2011-04-27.
hjk's avatar
hjk committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
                // "{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.
                QByteArray ba = result.toString();
                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);
Robert Loehning's avatar
Robert Loehning committed
574
                ba.remove(pos1, pos3 - pos1 + 1);
hjk's avatar
hjk committed
575 576 577
                result = GdbMi();
                result.fromString(ba);
                BreakHandler *handler = breakHandler();
578
                BreakpointModelId id;
hjk's avatar
hjk committed
579 580
                BreakpointResponse br;
                foreach (const GdbMi &bkpt, result.children()) {
581
                    const QByteArray nr = bkpt["number"].data();
582
                    BreakpointResponseId rid(nr);
583
                    if (!isHiddenBreakpoint(rid)) {
584 585 586 587 588 589
                        if (nr.contains('.')) {
                            // A sub-breakpoint.
                            BreakpointResponse sub;
                            updateResponse(sub, bkpt);
                            sub.id = rid;
                            sub.type = br.type;
590
                            handler->insertSubBreakpoint(id, sub);
591 592
                        } else {
                            // A primary breakpoint.
593 594 595 596 597
                            id = handler->findBreakpointByResponseId(rid);
                            //qDebug() << "NR: " << nr << "RID: " << rid
                            //    << "ID: " << id;
                            //BreakpointModelId id =
                            //    handler->findBreakpointByResponseId(rid);
598 599
                            br = handler->response(id);
                            updateResponse(br, bkpt);
600
                            handler->setResponse(id, br);
601
                        }
hjk's avatar
hjk committed
602 603
                    }
                }
604
                m_hasBreakpointNotifications = true;
605 606 607
            } else if (asyncClass == "breakpoint-created") {
                // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
                //  addr="<PENDING>",pending="main",times="0",
608 609 610
                //  original-location="main"}}" -- or --
                // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
                //  what="*0xbfffed48",times="0",original-location="*0xbfffed48"
611
                BreakHandler *handler = breakHandler();
612 613
                foreach (const GdbMi &bkpt, result.children()) {
                    BreakpointResponse br;
614
                    br.type = BreakpointByFileAndLine;
615
                    updateResponse(br, bkpt);
616
                    handler->handleAlienBreakpoint(br, this);
617 618 619 620 621
                }
            } else if (asyncClass == "breakpoint-deleted") {
                // "breakpoint-deleted" "{id="1"}"
                // New in FSF gdb since 2011-04-27.
                BreakHandler *handler = breakHandler();
622
                QByteArray nr = result["id"].data();
623 624
                BreakpointResponseId rid(nr);
                BreakpointModelId id = handler->findBreakpointByResponseId(rid);
625 626 627 628 629 630 631
                if (id.isValid()) {
                    // 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 (!handler->isOneShot(id))
                        handler->removeAlienBreakpoint(id);
                }
632 633 634
            } else if (asyncClass == "cmd-param-changed") {
                // New since 2012-08-09
                //  "{param="debug remote",value="1"}"
635 636 637
            } else if (asyncClass == "memory-changed") {
                // New since 2013
                //   "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
638
            } else {
639
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
640
                    << asyncClass << result.toString();
641
            }
642 643
            break;
        }
644

645
        case '~': {
646 647
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
648 649

            // Parse pid from noise.
650
            if (!inferiorPid()) {
651 652 653 654
                // Linux/Mac gdb: [New [Tt]hread 0x545 (LWP 4554)]
                static QRegExp re1(_("New .hread 0x[0-9a-f]+ \\(LWP ([0-9]*)\\)"));
                // MinGW 6.8: [New thread 2437.0x435345]
                static QRegExp re2(_("New .hread ([0-9]+)\\.0x[0-9a-f]*"));
hjk's avatar
hjk committed
655 656 657
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
658 659 660 661 662
                QTC_ASSERT(re1.isValid() && re2.isValid(), return);
                if (re1.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re1.cap(1));
                else if (re2.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re2.cap(1));
663 664
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
665
            }
666 667

            // Show some messages to give the impression something happens.
668
            if (data.startsWith("Reading symbols from ")) {
669
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
670
                progressPing();
671
                invalidateSourcesList();
672 673 674
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
675
                progressPing();
676
                showStatusMessage(_(data), 1000);
677 678 679 680
            } 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
681 682
                unsigned exCode;
                m_lastWinException = msgWinException(data, &exCode);
683
                showMessage(m_lastWinException, LogMisc);
684
                const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning;
685
                TaskHub::addTask(type, m_lastWinException, Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
686
            }
687 688 689 690

            if (data.startsWith("QMLBP:")) {
                int pos1 = 6;
                int pos2 = data.indexOf(' ', pos1);
691
                m_qmlBreakpointResponseId2 = BreakpointResponseId(data.mid(pos1, pos2 - pos1));
692 693 694 695
                //qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
                //qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
            }

696 697
            break;
        }
698

699
        case '@': {
700
            readDebugeeOutput(GdbMi::parseCString(from, to));
701
            break;
con's avatar
con committed
702 703
        }

704 705 706 707 708 709
        case '&': {
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingLogStreamOutput += data;
            // On Windows, the contents seem to depend on the debugger
            // version and/or OS version used.
            if (data.startsWith("warning:"))
710
                showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
711

712
            if (isGdbConnectionError(data)) {
713
                notifyInferiorExited();
714
                break;
715
            }
716

717 718 719 720 721 722 723 724 725 726 727 728 729
            if (debuggerCore()->boolSetting(IdentifyDebugInfoPackages)) {
                // 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 ")) {
                    m_lastMissingDebugInfo = QString::fromLocal8Bit(data.mid(32));
                } else if (data.startsWith("Try: zypper")) {
                    QString cmd = QString::fromLocal8Bit(data.mid(4));

                    Task task(Task::Warning,
                        tr("Missing debug information for %1\nTry: %2")
                            .arg(m_lastMissingDebugInfo).arg(cmd),
                        FileName(), 0, Core::Id(Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO));

Nicolas Arnaud-Cormos's avatar
Nicolas Arnaud-Cormos committed
730
                    TaskHub::addTask(task);
731 732 733 734 735

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

con's avatar
con committed
738 739 740
            break;
        }

741
        case '^': {
hjk's avatar
hjk committed
742
            GdbResponse response;
con's avatar
con committed
743

hjk's avatar
hjk committed
744
            response.token = token;
con's avatar
con committed
745

746 747 748
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
749

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
750
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
751
            if (resultClass == "done")
hjk's avatar
hjk committed
752
                response.resultClass = GdbResultDone;
753
            else if (resultClass == "running")
hjk's avatar
hjk committed
754
                response.resultClass = GdbResultRunning;
755
            else if (resultClass == "connected")
hjk's avatar
hjk committed
756
                response.resultClass = GdbResultConnected;
757
            else if (resultClass == "error")
hjk's avatar
hjk committed
758
                response.resultClass = GdbResultError;
759
            else if (resultClass == "exit")
hjk's avatar
hjk committed
760
                response.resultClass = GdbResultExit;
761
            else
hjk's avatar
hjk committed
762
                response.resultClass = GdbResultUnknown;
con's avatar
con committed
763

764 765
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
766 767
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
768 769 770
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
771
                } else {
772
                    // Archer has this.
hjk's avatar
hjk committed
773 774
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
775 776
                }
            }
777 778 779

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
780 781
            response.logStreamOutput = m_pendingLogStreamOutput;
            response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
782 783 784
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

785
            handleResultRecord(&response);
786 787 788
            break;
        }
        default: {
789
            qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
790
            break;
con's avatar
con committed
791 792 793 794 795 796
        }
    }
}

void GdbEngine::readGdbStandardError()
{
ck's avatar
ck committed
797
    QByteArray err = gdbProc()->readAllStandardError();
798
    showMessage(_("UNEXPECTED GDB STDERR: " + err));
799 800
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
801 802
    if (err.startsWith("BFD: reopening"))
        return;
Jarek Kobus's avatar
Jarek Kobus committed
803
    qWarning() << "Unexpected GDB stderr:" << err;
con's avatar
con committed
804 805 806 807
}

void GdbEngine::readGdbStandardOutput()
{
808
    m_commandTimer.start(); // Restart timer.
809

810 811
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
812

hjk's avatar
hjk committed
813 814
    QByteArray out = gdbProc()->readAllStandardOutput();
    m_inbuffer.append(out);
con's avatar
con committed
815

816
    // This can trigger when a dialog starts a nested event loop.
817 818 819
    if (m_busy)
        return;

820 821
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
822
        int end = m_inbuffer.indexOf('\n', scan);
823 824 825 826 827
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
828
        scan = newstart;
829 830 831 832 833 834 835
        if (end == start)
            continue;
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
836
        m_busy = true;
hjk's avatar
hjk committed
837 838
        QByteArray ba = QByteArray::fromRawData(m_inbuffer.constData() + start, end - start);
        handleResponse(ba);
839
        m_busy = false;
con's avatar
con committed
840
    }
841
    m_inbuffer.clear();
con's avatar
con committed
842 843 844 845
}

void GdbEngine::interruptInferior()
{
hjk's avatar
hjk committed
846 847
    QTC_ASSERT(state() == InferiorStopRequested,
        qDebug() << "INTERRUPT INFERIOR: " << state(); return);
848

849
    if (usesExecInterrupt()) {
hjk's avatar
hjk committed
850
        postCommand("-exec-interrupt", Immediate);
851 852 853
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
854
        interruptInferior2();
855
    }
con's avatar
con committed
856 857
}

858 859
void GdbEngine::interruptInferiorTemporarily()
{
860
    foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
861
        if (cmd.flags & LosesChild) {
hjk's avatar
hjk committed
862