gdbengine.cpp 183 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
con's avatar
con committed
32

33 34
#define QT_NO_CAST_FROM_ASCII

con's avatar
con committed
35
#include "gdbengine.h"
36

Friedemann Kleint's avatar
Friedemann Kleint committed
37
#include "debuggerstartparameters.h"
38
#include "debuggerinternalconstants.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
39
#include "disassemblerlines.h"
40 41
#include "attachgdbadapter.h"
#include "coregdbadapter.h"
ck's avatar
ck committed
42
#include "localplaingdbadapter.h"
43
#include "termgdbadapter.h"
ck's avatar
ck committed
44 45
#include "remotegdbserveradapter.h"
#include "remoteplaingdbadapter.h"
46
#include "codagdbadapter.h"
con's avatar
con committed
47

48
#include "debuggeractions.h"
con's avatar
con committed
49
#include "debuggerconstants.h"
50 51 52
#include "debuggercore.h"
#include "debuggerplugin.h"
#include "debuggerrunner.h"
53
#include "debuggerstringutils.h"
54
#include "debuggertooltipmanager.h"
55
#include "disassembleragent.h"
con's avatar
con committed
56
#include "gdbmi.h"
57 58 59
#include "gdboptionspage.h"
#include "memoryagent.h"
#include "watchutils.h"
con's avatar
con committed
60 61 62 63

#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
64
#include "snapshothandler.h"
65
#include "sourcefileshandler.h"
con's avatar
con committed
66
#include "stackhandler.h"
hjk's avatar
hjk committed
67
#include "threadshandler.h"
con's avatar
con committed
68
#include "watchhandler.h"
69
#include "debuggersourcepathmappingwidget.h"
70

71 72 73
#ifdef Q_OS_WIN
#    include "dbgwinutils.h"
#endif
74
#include "logwindow.h"
con's avatar
con committed
75

76
#include <coreplugin/icore.h>
77
#include <coreplugin/ifile.h>
78
#include <projectexplorer/abi.h>
79
#include <projectexplorer/projectexplorerconstants.h>
80 81
#include <texteditor/itexteditor.h>
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
82

83
#include <QtCore/QCoreApplication>
con's avatar
con committed
84 85 86
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
87
#include <QtCore/QMetaObject>
con's avatar
con committed
88 89
#include <QtCore/QTime>
#include <QtCore/QTimer>
90
#include <QtCore/QTemporaryFile>
91
#include <QtCore/QTextStream>
con's avatar
con committed
92 93

#include <QtGui/QAction>
94
#include <QtGui/QDialogButtonBox>
con's avatar
con committed
95 96 97
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
98
#include <QtGui/QPushButton>
con's avatar
con committed
99

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
100
#ifdef Q_OS_UNIX
con's avatar
con committed
101 102 103
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
104
#include <ctype.h>
con's avatar
con committed
105

hjk's avatar
hjk committed
106 107
using namespace ProjectExplorer;

108 109
namespace Debugger {
namespace Internal {
con's avatar
con committed
110

111 112 113
class GdbToolTipContext : public DebuggerToolTipContext
{
public:
114 115
    GdbToolTipContext(const DebuggerToolTipContext &c) :
        DebuggerToolTipContext(c), editor(0) {}
116 117 118

    QPoint mousePosition;
    QString expression;
119
    Core::IEditor *editor;
120 121
};

122 123 124
enum { debugPending = 0 };

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

126 127
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

128
QByteArray GdbEngine::tooltipIName(const QString &exp)
129
{
130
    return "tooltip." + exp.toLatin1().toHex();
131 132
}

hjk's avatar
hjk committed
133
static bool stateAcceptsGdbCommands(DebuggerState state)
hjk's avatar
hjk committed
134
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
135
    switch (state) {
hjk's avatar
hjk committed
136
    case EngineSetupRequested:
137 138
    case EngineSetupOk:
    case EngineSetupFailed:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
139
    case InferiorUnrunnable:
hjk's avatar
hjk committed
140
    case InferiorSetupRequested:
hjk's avatar
hjk committed
141
    case InferiorSetupFailed:
hjk's avatar
hjk committed
142 143 144 145 146 147 148 149
    case EngineRunRequested:
    case InferiorRunRequested:
    case InferiorRunOk:
    case InferiorStopRequested:
    case InferiorStopOk:
    case InferiorShutdownRequested:
    case EngineShutdownRequested:
    case InferiorShutdownOk:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
150 151 152 153
    case InferiorShutdownFailed:
        return true;
    case DebuggerNotReady:
    case InferiorStopFailed:
154
    case InferiorSetupOk:
hjk's avatar
hjk committed
155
    case EngineRunFailed:
hjk's avatar
hjk committed
156
    case InferiorExitOk:
hjk's avatar
hjk committed
157 158 159 160
    case InferiorRunFailed:
    case EngineShutdownOk:
    case EngineShutdownFailed:
    case DebuggerFinished:
161
        return false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
162 163 164
    }
    return false;
}
hjk's avatar
hjk committed
165

con's avatar
con committed
166 167 168 169 170 171
static int &currentToken()
{
    static int token = 0;
    return token;
}

hjk's avatar
hjk committed
172
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
173
{
hjk's avatar
hjk committed
174
    GdbMi output = response.data.findChild("consolestreamoutput");
175 176 177 178 179 180 181 182 183 184
    QByteArray out = output.data();
    // 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);
}

con's avatar
con committed
185 186 187 188 189 190
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

191 192 193
GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
        DebuggerEngine *masterEngine)
  : DebuggerEngine(startParameters, masterEngine)
con's avatar
con committed
194
{
195
    setObjectName(_("GdbEngine"));
196

197
    m_busy = false;
198 199 200 201 202
    m_gdbAdapter = 0;
    m_debuggingHelperState = DebuggingHelperUninitialized;
    m_gdbVersion = 100;
    m_gdbBuildVersion = -1;
    m_isMacGdb = false;
203
    m_hasBreakpointNotifications = false;
204 205 206 207 208 209 210 211 212
    m_hasPython = false;
    m_registerNamesListed = false;
    m_hasInferiorThreadList = false;
    m_sourcesListUpdating = false;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingWatchRequests = 0;
    m_pendingBreakpointRequests = 0;
    m_commandsDoneCallback = 0;
213
    m_stackNeeded = false;
214
    m_preparedForQmlBreak = false;
215
    m_disassembleUsesComma = false;
hjk's avatar
hjk committed
216
    m_qFatalBreakpointNumber = 0;
217
    m_actingOnExpectedStop = false;
218

219 220
    invalidateSourcesList();

ck's avatar
ck committed
221
    m_gdbAdapter = createAdapter();
222

223 224 225
    m_commandTimer.setSingleShot(true);
    connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));

hjk's avatar
hjk committed
226
    connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
227
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
228
    connect(debuggerCore()->action(SortStructMembers), SIGNAL(valueChanged(QVariant)),
229
            SLOT(reloadLocals()));
230 231 232 233
    connect(debuggerCore()->action(ShowStdNamespace), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
    connect(debuggerCore()->action(ShowQtNamespace), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
234
    connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
235
            SLOT(createFullBacktrace()));
236
}
237

238 239
DebuggerStartMode GdbEngine::startMode() const
{
240
    return startParameters().startMode;
con's avatar
con committed
241 242
}

ck's avatar
ck committed
243 244 245 246 247
AbstractGdbProcess *GdbEngine::gdbProc() const
{
    return m_gdbAdapter->gdbProc();
}

con's avatar
con committed
248 249
GdbEngine::~GdbEngine()
{
250
    // Prevent sending error messages afterwards.
ck's avatar
ck committed
251 252
    if (m_gdbAdapter)
        disconnect(gdbProc(), 0, this, 0);
253
    delete m_gdbAdapter;
254
    m_gdbAdapter = 0;
hjk's avatar
hjk committed
255 256
}

257
QString GdbEngine::errorMessage(QProcess::ProcessError error)
con's avatar
con committed
258 259 260
{
    switch (error) {
        case QProcess::FailedToStart:
Jarek Kobus's avatar
Jarek Kobus committed
261
            return tr("The gdb process failed to start. Either the "
con's avatar
con committed
262
                "invoked program '%1' is missing, or you may have insufficient "
263 264
                "permissions to invoke the program.\n%2")
                .arg(m_gdb, gdbProc()->errorString());
con's avatar
con committed
265
        case QProcess::Crashed:
Jarek Kobus's avatar
Jarek Kobus committed
266
            return tr("The gdb process crashed some time after starting "
con's avatar
con committed
267 268
                "successfully.");
        case QProcess::Timedout:
269
            return tr("The last waitFor...() function timed out. "
con's avatar
con committed
270 271 272
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
        case QProcess::WriteError:
273
            return tr("An error occurred when attempting to write "
Jarek Kobus's avatar
Jarek Kobus committed
274
                "to the gdb process. For example, the process may not be running, "
con's avatar
con committed
275 276
                "or it may have closed its input channel.");
        case QProcess::ReadError:
277
            return tr("An error occurred when attempting to read from "
Jarek Kobus's avatar
Jarek Kobus committed
278
                "the gdb process. For example, the process may not be running.");
con's avatar
con committed
279
        default:
Jarek Kobus's avatar
Jarek Kobus committed
280
            return tr("An unknown error in the gdb process occurred. ");
con's avatar
con committed
281 282 283 284 285 286 287
    }
}

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
288
    Q_UNUSED(to)
con's avatar
con committed
289 290 291 292 293 294 295 296 297 298 299
    // 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

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
// Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
// and return an exception message
static inline QString msgWinException(const QByteArray &data)
{
    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);
    const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
    QString rc;
    QTextStream str(&rc);
    str << GdbEngine::tr("An exception was triggered: ");
#ifdef Q_OS_WIN
    formatWindowsException(exCode, address, 0, 0, 0, str);
#else
    Q_UNUSED(exCode)
    Q_UNUSED(address)
#endif
    str << '.';
    return rc;
}

324 325
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
326 327
    QString msg = m_outputCodec->toUnicode(data.constData(), data.length(),
        &m_outputCodecState);
328
    showMessage(msg, AppOutput);
329 330
}

hjk's avatar
hjk committed
331 332 333 334 335 336
static bool isNameChar(char c)
{
    // could be 'stopped' or 'shlibs-added'
    return (c >= 'a' && c <= 'z') || c == '-';
}

337
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
338
{
339
    showMessage(QString::fromLocal8Bit(buff, buff.length()), LogOutput);
con's avatar
con committed
340

341 342
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
343

344 345 346
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
347

348
    int token = -1;
349
    // Token is a sequence of numbers.
350 351
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
352
            break;
353 354 355 356
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
    }
con's avatar
con committed
357

358
    // Next char decides kind of response.
359 360 361 362 363 364 365 366 367 368 369 370
    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
371

hjk's avatar
hjk committed
372
            GdbMi result;
373 374
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
375
                if (*from != ',') {
376 377
                    // happens on archer where we get
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb)
hjk's avatar
hjk committed
378
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
379 380 381 382 383
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
384 385 386
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
387 388
                }
            }
389
            if (asyncClass == "stopped") {
390
                handleStopResponse(result);
391 392
                m_pendingLogStreamOutput.clear();
                m_pendingConsoleStreamOutput.clear();
393 394
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
395 396 397 398
            } 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",
399
                // symbols-loaded="0"
hjk's avatar
hjk committed
400
                QByteArray id = result.findChild("id").data();
401
                if (!id.isEmpty())
402
                    showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
403
                progressPing();
404
                invalidateSourcesList();
hjk's avatar
hjk committed
405 406 407 408
            } 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
409
                QByteArray id = result.findChild("id").data();
410
                progressPing();
411
                showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
412
                invalidateSourcesList();
413 414
            } else if (asyncClass == "thread-group-added") {
                // 7.1-symbianelf has "{id="i1"}"
415 416 417
            } 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
418 419
                // *-started seems to be standard in 7.1, but in early
                // 7.0.x, there was a *-created instead.
420
                progressPing();
ck's avatar
ck committed
421
                // 7.1.50 has thread-group-started,id="i1",pid="3529"
hjk's avatar
hjk committed
422
                QByteArray id = result.findChild("id").data();
423
                showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
ck's avatar
ck committed
424 425 426 427 428 429 430
                int pid = id.toInt();
                if (!pid) {
                    id = result.findChild("pid").data();
                    pid = id.toInt();
                }
                if (pid)
                    notifyInferiorPid(pid);
431
            } else if (asyncClass == "thread-created") {
432
                //"{id="1",group-id="28902"}"
hjk's avatar
hjk committed
433
                QByteArray id = result.findChild("id").data();
434
                showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
435
            } else if (asyncClass == "thread-group-exited") {
436
                // Archer has "{id="28902"}"
hjk's avatar
hjk committed
437
                QByteArray id = result.findChild("id").data();
438
                showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
439
            } else if (asyncClass == "thread-exited") {
440
                //"{id="1",group-id="28902"}"
hjk's avatar
hjk committed
441 442
                QByteArray id = result.findChild("id").data();
                QByteArray groupid = result.findChild("group-id").data();
443
                showStatusMessage(tr("Thread %1 in group %2 exited")
hjk's avatar
hjk committed
444
                    .arg(_(id)).arg(_(groupid)), 1000);
hjk's avatar
hjk committed
445
            } else if (asyncClass == "thread-selected") {
hjk's avatar
hjk committed
446
                QByteArray id = result.findChild("id").data();
447
                showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
448
                //"{id="2"}"
449 450
            } else if (m_isMacGdb && asyncClass == "shlibs-updated") {
                // Apple's gdb announces updated libs.
451
                invalidateSourcesList();
452 453
            } else if (m_isMacGdb && asyncClass == "shlibs-added") {
                // Apple's gdb announces added libs.
454 455 456 457 458
                // {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=""}}
459
                invalidateSourcesList();
460 461 462 463 464
            } 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",
465
                // line="1584",shlib="/../libFoo_debug.dylib",times="0"}
466 467
                const GdbMi bkpt = result.findChild("bkpt");
                const int number = bkpt.findChild("number").data().toInt();
hjk's avatar
hjk committed
468 469 470 471 472 473
                if (!isQmlStepBreakpoint(number)) {
                    BreakHandler *handler = breakHandler();
                    BreakpointId id = handler->findBreakpointByNumber(number);
                    BreakpointResponse br = handler->response(id);
                    updateResponse(br, bkpt);
                    handler->setResponse(id, br);
474
                    attemptAdjustBreakpointLocation(id);
475
                }
476 477
            } else if (asyncClass == "breakpoint-modified") {
                // New in FSF gdb since 2011-04-27.
hjk's avatar
hjk committed
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
                // "{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);
                ba.replace(pos1, pos3 - pos1 + 1, "");
                result = GdbMi();
                result.fromString(ba);
                BreakHandler *handler = breakHandler();
                BreakpointId id = BreakpointId(-1);
                BreakpointResponse br;
                foreach (const GdbMi &bkpt, result.children()) {
                    const QByteArray nr = bkpt.findChild("number").data();
                    if (nr.contains(".")) {
                        // A sub-breakpoint.
                        int subNumber = nr.mid(nr.indexOf('.') + 1).toInt();
                        BreakpointResponse sub;
                        updateResponse(sub, bkpt);
                        sub.number = br.number;
                        sub.type = br.type;
                        sub.subNumber = subNumber;
                        sub.extra.clear();
                        handler->appendSubBreakpoint(id, sub);
                    } else {
                        // A primary breakpoint.
                        id = handler->findBreakpointByNumber(nr.toInt());
                        br = handler->response(id);
                        updateResponse(br, bkpt);
                    }
                }
                if (!isQmlStepBreakpoint(br.number)) {
                    handler->setResponse(id, br);
520 521 522
                    attemptAdjustBreakpointLocation(id);
                }
                m_hasBreakpointNotifications = true;
523
            } else {
524
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
525
                    << asyncClass << result.toString();
526
            }
527 528
            break;
        }
529

530
        case '~': {
531 532
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
533 534

            // Parse pid from noise.
535
            if (!inferiorPid()) {
536 537 538 539
                // 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
540 541 542
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
543 544 545 546 547
                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));
548 549
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
550
            }
551 552

            // Show some messages to give the impression something happens.
553
            if (data.startsWith("Reading symbols from ")) {
554
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
555
                progressPing();
556
                invalidateSourcesList();
557 558 559
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
560
                progressPing();
561
                showStatusMessage(_(data), 1000);
562 563 564 565 566 567
            } 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
                m_lastWinException = msgWinException(data);
                showMessage(m_lastWinException, LogMisc);
568
            }
569 570 571 572 573 574 575 576 577

            if (data.startsWith("QMLBP:")) {
                int pos1 = 6;
                int pos2 = data.indexOf(' ', pos1);
                m_qmlBreakpointNumbers[2] = data.mid(pos1, pos2 - pos1).toInt();
                //qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
                //qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
            }

578 579
            break;
        }
580

581
        case '@': {
582
            readDebugeeOutput(GdbMi::parseCString(from, to));
583
            break;
con's avatar
con committed
584 585
        }

586 587 588 589 590 591
        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:"))
592
                showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
con's avatar
con committed
593 594 595
            break;
        }

596
        case '^': {
hjk's avatar
hjk committed
597
            GdbResponse response;
con's avatar
con committed
598

hjk's avatar
hjk committed
599
            response.token = token;
con's avatar
con committed
600

601 602 603
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
604

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
605
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
606
            if (resultClass == "done") {
hjk's avatar
hjk committed
607
                response.resultClass = GdbResultDone;
608
            } else if (resultClass == "running") {
hjk's avatar
hjk committed
609
                response.resultClass = GdbResultRunning;
610
            } else if (resultClass == "connected") {
hjk's avatar
hjk committed
611
                response.resultClass = GdbResultConnected;
612
            } else if (resultClass == "error") {
hjk's avatar
hjk committed
613
                response.resultClass = GdbResultError;
614
            } else if (resultClass == "exit") {
hjk's avatar
hjk committed
615
                response.resultClass = GdbResultExit;
616
            } else {
hjk's avatar
hjk committed
617
                response.resultClass = GdbResultUnknown;
618
            }
con's avatar
con committed
619

620 621
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
622 623
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
624 625 626
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
627
                } else {
628
                    // Archer has this.
hjk's avatar
hjk committed
629 630
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
631 632
                }
            }
633 634 635

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
hjk's avatar
hjk committed
636
            response.data.setStreamOutput("logstreamoutput",
637
                m_pendingLogStreamOutput);
hjk's avatar
hjk committed
638
            response.data.setStreamOutput("consolestreamoutput",
639 640 641 642
                m_pendingConsoleStreamOutput);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

643
            handleResultRecord(&response);
644 645 646 647 648
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
649 650 651 652 653 654
        }
    }
}

void GdbEngine::readGdbStandardError()
{
ck's avatar
ck committed
655
    QByteArray err = gdbProc()->readAllStandardError();
656
    showMessage(_("UNEXPECTED GDB STDERR: " + err));
657 658
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
659 660
    if (err.startsWith("BFD: reopening"))
        return;
Jarek Kobus's avatar
Jarek Kobus committed
661
    qWarning() << "Unexpected GDB stderr:" << err;
con's avatar
con committed
662 663 664 665
}

void GdbEngine::readGdbStandardOutput()
{
666
    m_commandTimer.start(); // Restart timer.
667

668 669
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
670

ck's avatar
ck committed
671
    m_inbuffer.append(gdbProc()->readAllStandardOutput());
con's avatar
con committed
672

673
    // This can trigger when a dialog starts a nested event loop.
674 675 676
    if (m_busy)
        return;

677 678
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
679
        int end = m_inbuffer.indexOf('\n', scan);
680 681 682 683 684
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
685
        scan = newstart;
686 687
        if (end == start)
            continue;
Tobias Hunger's avatar
Tobias Hunger committed
688
#        if defined(Q_OS_WIN)
689 690 691 692 693
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
Tobias Hunger's avatar
Tobias Hunger committed
694
#        endif
695
        m_busy = true;
696
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
697
        m_busy = false;
con's avatar
con committed
698
    }
699
    m_inbuffer.clear();
con's avatar
con committed
700 701 702 703
}

void GdbEngine::interruptInferior()
{
hjk's avatar
hjk committed
704 705
    QTC_ASSERT(state() == InferiorStopRequested,
        qDebug() << "INTERRUPT INFERIOR: " << state(); return);
706 707 708 709 710 711 712 713

    if (debuggerCore()->boolSetting(TargetAsync)) {
        postCommand("-exec-interrupt");
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
        m_gdbAdapter->interruptInferior();
    }
con's avatar
con committed
714 715
}

716 717
void GdbEngine::interruptInferiorTemporarily()
{
718
    foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
719
        if (cmd.flags & LosesChild) {
hjk's avatar
hjk committed
720
            notifyInferiorIll();
721
            return;
722
        }
723
    }
724
    requestInterruptInferior();
725 726
}

con's avatar
con committed
727 728
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
729
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
730
    if (pid == 0) {
731
        showMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
732 733
        return;
    }
734
    if (pid == inferiorPid())
con's avatar
con committed
735
        return;
736

737 738
    showMessage(_("FOUND PID %1").arg(pid));
    notifyInferiorPid(pid);
con's avatar
con committed
739 740
}

741
void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
hjk's avatar
hjk committed
742
                            const char *callbackName, const QVariant &cookie)
743 744 745 746
{
    postCommand(command, NoFlags, callback, callbackName, cookie);
}

747
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
748 749
                            AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
hjk's avatar
hjk committed
750 751 752
{
    GdbCommand cmd;
    cmd.command = command;
753
    cmd.flags = flags;
hjk's avatar
hjk committed
754 755 756 757 758 759
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

760
void GdbEngine::postCommand(const QByteArray &command, GdbCommandCallback callback,
761 762
                            const char *callbackName, const QVariant &cookie)
{
763
    postCommand(command, NoFlags, callback, callbackName, cookie);
764 765
}

766
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
767 768
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
769 770 771 772 773 774 775 776 777 778 779
{
    GdbCommand cmd;
    cmd.command = command;
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

void GdbEngine::postCommandHelper(const GdbCommand &cmd)
con's avatar
con committed
780
{
hjk's avatar
hjk committed
781
    if (!stateAcceptsGdbCommands(state())) {
782
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.command));
783
        showMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
784
            .arg(_(cmd.command)).arg(state()));
con's avatar
con committed
785 786 787
        return;
    }

788 789
    if (cmd.flags & RebuildWatchModel) {
        ++m_pendingWatchRequests;
790
        PENDING_DEBUG("   WATCH MODEL:" << cmd.command << "=>" << cmd.callbackName
791
                      << "INCREMENTS PENDING TO" << m_pendingWatchRequests);
792 793 794 795
    } else if (cmd.flags & RebuildBreakpointModel) {
        ++m_pendingBreakpointRequests;
        PENDING_DEBUG("   BRWAKPOINT MODEL:" << cmd.command << "=>" << cmd.callbackName
                      << "INCREMENTS PENDING TO" << m_pendingBreakpointRequests);
con's avatar
con committed
796
    } else {
797
        PENDING_DEBUG("   OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
798 799
                      << "LEAVES PENDING WATCH AT" << m_pendingWatchRequests
                      << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
con's avatar
con committed
800 801
    }

802 803 804 805 806 807
    // FIXME: clean up logic below
    if (cmd.flags & Immediate) {
        // This should always be sent.
        flushCommand(cmd);
    } else if ((cmd.flags & NeedsStop)
            || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
hjk's avatar
hjk committed
808 809 810
        if (state() == InferiorStopOk || state() == InferiorUnrunnable
            || state() == InferiorSetupRequested || state() == EngineSetupOk
            || state() == InferiorShutdownRequested) {
hjk's avatar
hjk committed
811 812 813 814
            // Can be safely sent now.
            flushCommand(cmd);
        } else {
            // Queue the commands that we cannot send at once.
815
            showMessage(_("QUEUING COMMAND " + cmd.command));
hjk's avatar
hjk committed
816
            m_commandsToRunOnTemporaryBreak.append(cmd);
hjk's avatar
hjk committed
817 818 819 820
            if (state() == InferiorStopRequested) {
                if (cmd.flags & LosesChild) {
                    notifyInferiorIll();
                }
821
                showMessage(_("CHILD ALREADY BEING INTERRUPTED. STILL HOPING."));
822 823
                // Calling shutdown() here breaks all situations where two
                // NeedsStop commands are issued in quick succession.
hjk's avatar
hjk committed
824
            } else if (state() == InferiorRunOk) {
825
                showStatusMessage(tr("Stopping temporarily"), 1000);
826
                interruptInferiorTemporarily();
827
            } else {
hjk's avatar
hjk committed
828 829
                qDebug() << "ATTEMPTING TO QUEUE COMMAND "
                    << cmd.command << "IN INAPPROPRIATE STATE" << state();
830
            }
hjk's avatar
hjk committed
831
        }
hjk's avatar
hjk committed
832
    } else if (!cmd.command.isEmpty()) {
833
        flushCommand(cmd);
con's avatar
con committed
834 835 836
    }
}

837 838
void GdbEngine::flushQueuedCommands()
{
839
    showStatusMessage(tr("Processing queued commands"), 1000);
840 841
    while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
        GdbCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
842
        showMessage(_("RUNNING QUEUED COMMAND " + cmd.command + ' '
843
            + cmd.callbackName));
844 845 846 847
        flushCommand(cmd);
    }
}