gdbengine.cpp 195 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@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
29
** Nokia at qt-info@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
#include "hostutils.h"
71
#include "logwindow.h"
con's avatar
con committed
72

73
#include <coreplugin/icore.h>
74
#include <coreplugin/idocument.h>
75
#include <extensionsystem/pluginmanager.h>
76
#include <projectexplorer/abi.h>
77
#include <projectexplorer/projectexplorerconstants.h>
78 79
#include <projectexplorer/taskhub.h>
#include <projectexplorer/itaskhandler.h>
80 81
#include <texteditor/itexteditor.h>
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
82

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

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

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

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

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

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

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

123 124 125
enum { debugPending = 0 };

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

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

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

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

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

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

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
///////////////////////////////////////////////////////////////////////
//
// Debuginfo Taskhandler
//
///////////////////////////////////////////////////////////////////////

class DebugInfoTask
{
public:
    QString command;
};

class DebugInfoTaskHandler : public  ProjectExplorer::ITaskHandler
{
public:
    DebugInfoTaskHandler(GdbEngine *engine)
        : ITaskHandler(_("Debuginfo")), m_engine(engine)
    {}

    bool canHandle(const Task &task)
    {
        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;
    }

    QAction *createAction(QObject *parent = 0)
    {
hjk's avatar
hjk committed
221 222
        QAction *action = new QAction(DebuggerPlugin::tr("Install &Debug Information"), parent);
        action->setToolTip(DebuggerPlugin::tr("This tries to install missing debug information."));
223 224 225 226 227 228 229 230
        return action;
    }

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

con's avatar
con committed
231 232 233 234 235 236
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

237 238
GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters,
        DebuggerEngine *masterEngine)
239
  : DebuggerEngine(startParameters, CppLanguage, masterEngine)
con's avatar
con committed
240
{
241
    setObjectName(_("GdbEngine"));
242

243
    m_busy = false;
244 245 246 247 248
    m_gdbAdapter = 0;
    m_debuggingHelperState = DebuggingHelperUninitialized;
    m_gdbVersion = 100;
    m_gdbBuildVersion = -1;
    m_isMacGdb = false;
249
    m_isQnxGdb = false;
250
    m_hasBreakpointNotifications = false;
251 252 253 254 255
    m_hasPython = false;
    m_registerNamesListed = false;
    m_hasInferiorThreadList = false;
    m_sourcesListUpdating = false;
    m_oldestAcceptableToken = -1;
256
    m_nonDiscardableCount = 0;
257 258 259 260
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingWatchRequests = 0;
    m_pendingBreakpointRequests = 0;
    m_commandsDoneCallback = 0;
261
    m_stackNeeded = false;
262
    m_preparedForQmlBreak = false;
263
    m_disassembleUsesComma = false;
264
    m_actingOnExpectedStop = false;
265
    m_fullStartDone = false;
266

267 268
    invalidateSourcesList();

ck's avatar
ck committed
269
    m_gdbAdapter = createAdapter();
270

271 272 273
    m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
    ExtensionSystem::PluginManager::instance()->addObject(m_debugInfoTaskHandler);

274 275 276
    m_commandTimer.setSingleShot(true);
    connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));

hjk's avatar
hjk committed
277
    connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
278
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
279
    connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
280
            SLOT(createFullBacktrace()));
281
}
282

283 284
DebuggerStartMode GdbEngine::startMode() const
{
285
    return startParameters().startMode;
con's avatar
con committed
286 287
}

ck's avatar
ck committed
288 289 290 291 292
AbstractGdbProcess *GdbEngine::gdbProc() const
{
    return m_gdbAdapter->gdbProc();
}

con's avatar
con committed
293 294
GdbEngine::~GdbEngine()
{
295 296 297 298
    ExtensionSystem::PluginManager::instance()->removeObject(m_debugInfoTaskHandler);
    delete m_debugInfoTaskHandler;
    m_debugInfoTaskHandler = 0;

299
    // Prevent sending error messages afterwards.
ck's avatar
ck committed
300 301
    if (m_gdbAdapter)
        disconnect(gdbProc(), 0, this, 0);
302
    delete m_gdbAdapter;
303
    m_gdbAdapter = 0;
hjk's avatar
hjk committed
304 305
}

306
QString GdbEngine::errorMessage(QProcess::ProcessError error)
con's avatar
con committed
307 308 309
{
    switch (error) {
        case QProcess::FailedToStart:
Jarek Kobus's avatar
Jarek Kobus committed
310
            return tr("The gdb process failed to start. Either the "
con's avatar
con committed
311
                "invoked program '%1' is missing, or you may have insufficient "
312 313
                "permissions to invoke the program.\n%2")
                .arg(m_gdb, gdbProc()->errorString());
con's avatar
con committed
314
        case QProcess::Crashed:
315 316 317 318 319
            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
320
        case QProcess::Timedout:
321
            return tr("The last waitFor...() function timed out. "
con's avatar
con committed
322 323 324
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
        case QProcess::WriteError:
325
            return tr("An error occurred when attempting to write "
Jarek Kobus's avatar
Jarek Kobus committed
326
                "to the gdb process. For example, the process may not be running, "
con's avatar
con committed
327 328
                "or it may have closed its input channel.");
        case QProcess::ReadError:
329
            return tr("An error occurred when attempting to read from "
Jarek Kobus's avatar
Jarek Kobus committed
330
                "the gdb process. For example, the process may not be running.");
con's avatar
con committed
331
        default:
Jarek Kobus's avatar
Jarek Kobus committed
332
            return tr("An unknown error in the gdb process occurred. ");
con's avatar
con committed
333 334 335 336 337 338 339
    }
}

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
340
    Q_UNUSED(to)
con's avatar
con committed
341 342 343 344 345 346 347 348 349 350 351
    // 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

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
// 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;
}

376 377
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
378 379
    QString msg = m_outputCodec->toUnicode(data.constData(), data.length(),
        &m_outputCodecState);
380
    showMessage(msg, AppOutput);
381 382
}

hjk's avatar
hjk committed
383 384 385 386 387 388
static bool isNameChar(char c)
{
    // could be 'stopped' or 'shlibs-added'
    return (c >= 'a' && c <= 'z') || c == '-';
}

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

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

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

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

410
    // Next char decides kind of response.
411 412 413 414 415 416 417 418 419 420 421 422
    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
423

hjk's avatar
hjk committed
424
            GdbMi result;
425 426
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
427
                if (*from != ',') {
428 429
                    // happens on archer where we get
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb)
hjk's avatar
hjk committed
430
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
431 432 433 434 435
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
436 437 438
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
439 440
                }
            }
441
            if (asyncClass == "stopped") {
442
                handleStopResponse(result);
443 444
                m_pendingLogStreamOutput.clear();
                m_pendingConsoleStreamOutput.clear();
445 446
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
447 448 449 450
            } 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",
451
                // symbols-loaded="0"
hjk's avatar
hjk committed
452
                QByteArray id = result.findChild("id").data();
453
                if (!id.isEmpty())
454
                    showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
455
                progressPing();
456
                invalidateSourcesList();
hjk's avatar
hjk committed
457 458 459 460
            } 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
461
                QByteArray id = result.findChild("id").data();
462
                progressPing();
463
                showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
464
                invalidateSourcesList();
465 466
            } else if (asyncClass == "thread-group-added") {
                // 7.1-symbianelf has "{id="i1"}"
467 468 469
            } 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
470 471
                // *-started seems to be standard in 7.1, but in early
                // 7.0.x, there was a *-created instead.
472
                progressPing();
ck's avatar
ck committed
473
                // 7.1.50 has thread-group-started,id="i1",pid="3529"
hjk's avatar
hjk committed
474
                QByteArray id = result.findChild("id").data();
475
                showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
ck's avatar
ck committed
476 477 478 479 480 481 482
                int pid = id.toInt();
                if (!pid) {
                    id = result.findChild("pid").data();
                    pid = id.toInt();
                }
                if (pid)
                    notifyInferiorPid(pid);
483
                handleThreadGroupCreated(result);
484
            } else if (asyncClass == "thread-created") {
485
                //"{id="1",group-id="28902"}"
hjk's avatar
hjk committed
486
                QByteArray id = result.findChild("id").data();
487
                showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
488
            } else if (asyncClass == "thread-group-exited") {
489
                // Archer has "{id="28902"}"
hjk's avatar
hjk committed
490
                QByteArray id = result.findChild("id").data();
491
                showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
492
                handleThreadGroupExited(result);
493
            } else if (asyncClass == "thread-exited") {
494
                //"{id="1",group-id="28902"}"
hjk's avatar
hjk committed
495 496
                QByteArray id = result.findChild("id").data();
                QByteArray groupid = result.findChild("group-id").data();
497
                showStatusMessage(tr("Thread %1 in group %2 exited")
hjk's avatar
hjk committed
498
                    .arg(_(id)).arg(_(groupid)), 1000);
hjk's avatar
hjk committed
499
            } else if (asyncClass == "thread-selected") {
hjk's avatar
hjk committed
500
                QByteArray id = result.findChild("id").data();
501
                showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
502
                //"{id="2"}"
503 504
            } else if (m_isMacGdb && asyncClass == "shlibs-updated") {
                // Apple's gdb announces updated libs.
505
                invalidateSourcesList();
506 507
            } else if (m_isMacGdb && asyncClass == "shlibs-added") {
                // Apple's gdb announces added libs.
508 509 510 511 512
                // {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=""}}
513
                invalidateSourcesList();
514 515 516 517 518
            } 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",
519
                // line="1584",shlib="/../libFoo_debug.dylib",times="0"}
520
                const GdbMi bkpt = result.findChild("bkpt");
521 522
                const BreakpointResponseId rid(bkpt.findChild("number").data());
                if (!isQmlStepBreakpoint(rid)) {
hjk's avatar
hjk committed
523
                    BreakHandler *handler = breakHandler();
524
                    BreakpointModelId id = handler->findBreakpointByResponseId(rid);
hjk's avatar
hjk committed
525 526 527
                    BreakpointResponse br = handler->response(id);
                    updateResponse(br, bkpt);
                    handler->setResponse(id, br);
528
                    attemptAdjustBreakpointLocation(id);
529
                }
530 531
            } else if (asyncClass == "breakpoint-modified") {
                // New in FSF gdb since 2011-04-27.
hjk's avatar
hjk committed
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
                // "{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
547
                ba.remove(pos1, pos3 - pos1 + 1);
hjk's avatar
hjk committed
548 549 550
                result = GdbMi();
                result.fromString(ba);
                BreakHandler *handler = breakHandler();
551
                BreakpointModelId id;
hjk's avatar
hjk committed
552 553 554
                BreakpointResponse br;
                foreach (const GdbMi &bkpt, result.children()) {
                    const QByteArray nr = bkpt.findChild("number").data();
555
                    BreakpointResponseId rid(nr);
556
                    if (!isHiddenBreakpoint(rid)) {
557 558 559 560 561 562
                        if (nr.contains('.')) {
                            // A sub-breakpoint.
                            BreakpointResponse sub;
                            updateResponse(sub, bkpt);
                            sub.id = rid;
                            sub.type = br.type;
563
                            handler->insertSubBreakpoint(id, sub);
564 565
                        } else {
                            // A primary breakpoint.
566 567 568 569 570
                            id = handler->findBreakpointByResponseId(rid);
                            //qDebug() << "NR: " << nr << "RID: " << rid
                            //    << "ID: " << id;
                            //BreakpointModelId id =
                            //    handler->findBreakpointByResponseId(rid);
571 572
                            br = handler->response(id);
                            updateResponse(br, bkpt);
573
                            handler->setResponse(id, br);
574
                        }
hjk's avatar
hjk committed
575 576
                    }
                }
577
                m_hasBreakpointNotifications = true;
578 579 580
            } else if (asyncClass == "breakpoint-created") {
                // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
                //  addr="<PENDING>",pending="main",times="0",
581 582 583
                //  original-location="main"}}" -- or --
                // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
                //  what="*0xbfffed48",times="0",original-location="*0xbfffed48"
584
                BreakHandler *handler = breakHandler();
585 586
                foreach (const GdbMi &bkpt, result.children()) {
                    BreakpointResponse br;
587
                    br.type = BreakpointByFileAndLine;
588
                    updateResponse(br, bkpt);
589
                    handler->handleAlienBreakpoint(br, this);
590 591 592 593 594 595
                }
            } else if (asyncClass == "breakpoint-deleted") {
                // "breakpoint-deleted" "{id="1"}"
                // New in FSF gdb since 2011-04-27.
                BreakHandler *handler = breakHandler();
                QByteArray nr = result.findChild("id").data();
596 597
                BreakpointResponseId rid(nr);
                BreakpointModelId id = handler->findBreakpointByResponseId(rid);
598 599
                if (id.isValid())
                    handler->removeAlienBreakpoint(id);
600
            } else {
601
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
602
                    << asyncClass << result.toString();
603
            }
604 605
            break;
        }
606

607
        case '~': {
608 609
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
610 611

            // Parse pid from noise.
612
            if (!inferiorPid()) {
613 614 615 616
                // 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
617 618 619
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
620 621 622 623 624
                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));
625 626
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
627
            }
628 629

            // Show some messages to give the impression something happens.
630
            if (data.startsWith("Reading symbols from ")) {
631
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
632
                progressPing();
633
                invalidateSourcesList();
634 635 636
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
637
                progressPing();
638
                showStatusMessage(_(data), 1000);
639 640 641 642 643 644
            } 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);
645
            }
646 647 648 649

            if (data.startsWith("QMLBP:")) {
                int pos1 = 6;
                int pos2 = data.indexOf(' ', pos1);
650
                m_qmlBreakpointResponseId2 = BreakpointResponseId(data.mid(pos1, pos2 - pos1));
651 652 653 654
                //qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
                //qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
            }

655 656
            break;
        }
657

658
        case '@': {
659
            readDebugeeOutput(GdbMi::parseCString(from, to));
660
            break;
con's avatar
con committed
661 662
        }

663 664 665 666 667 668
        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:"))
669
                showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
670 671 672

            // Messages when the target exits (gdbserver)
            if (data.trimmed() == "Remote connection closed"
673 674
                    || data.trimmed() == "Remote communication error.  "
                                         "Target disconnected.: No error."
675 676 677
                    || data.trimmed() == "Quit") {
                notifyInferiorExited();
            }
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697

            // 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),
                    Utils::FileName(), 0, Core::Id("Debuginfo"));

                taskHub()->addTask(task);

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

con's avatar
con committed
698 699 700
            break;
        }

701
        case '^': {
hjk's avatar
hjk committed
702
            GdbResponse response;
con's avatar
con committed
703

hjk's avatar
hjk committed
704
            response.token = token;
con's avatar
con committed
705

706 707 708
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
709

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
710
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
711
            if (resultClass == "done") {
hjk's avatar
hjk committed
712
                response.resultClass = GdbResultDone;
713
            } else if (resultClass == "running") {
hjk's avatar
hjk committed
714
                response.resultClass = GdbResultRunning;
715
            } else if (resultClass == "connected") {
hjk's avatar
hjk committed
716
                response.resultClass = GdbResultConnected;
717
            } else if (resultClass == "error") {
hjk's avatar
hjk committed
718
                response.resultClass = GdbResultError;
719
            } else if (resultClass == "exit") {
hjk's avatar
hjk committed
720
                response.resultClass = GdbResultExit;
721
            } else {
hjk's avatar
hjk committed
722
                response.resultClass = GdbResultUnknown;
723
            }
con's avatar
con committed
724

725 726
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
727 728
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
729 730 731
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
732
                } else {
733
                    // Archer has this.
hjk's avatar
hjk committed
734 735
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
736 737
                }
            }
738 739 740

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
741 742
            response.logStreamOutput = m_pendingLogStreamOutput;
            response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
743 744 745
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

746
            handleResultRecord(&response);
747 748 749
            break;
        }
        default: {
750
            qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
751
            break;
con's avatar
con committed
752 753 754 755 756 757
        }
    }
}

void GdbEngine::readGdbStandardError()
{
ck's avatar
ck committed
758
    QByteArray err = gdbProc()->readAllStandardError();
759
    showMessage(_("UNEXPECTED GDB STDERR: " + err));
760 761
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
762 763
    if (err.startsWith("BFD: reopening"))
        return;
Jarek Kobus's avatar
Jarek Kobus committed
764
    qWarning() << "Unexpected GDB stderr:" << err;
con's avatar
con committed
765 766 767 768
}

void GdbEngine::readGdbStandardOutput()
{
769
    m_commandTimer.start(); // Restart timer.
770

771 772
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
773

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

776
    // This can trigger when a dialog starts a nested event loop.
777 778 779
    if (m_busy)
        return;

780 781
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
782
        int end = m_inbuffer.indexOf('\n', scan);
783 784 785 786 787
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
788
        scan = newstart;
789 790 791 792 793 794 795
        if (end == start)
            continue;
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
796
        m_busy = true;
797
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
798
        m_busy = false;
con's avatar
con committed
799
    }
800
    m_inbuffer.clear();
con's avatar
con committed
801 802 803 804
}

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

808
    if (usesExecInterrupt()) {
hjk's avatar
hjk committed
809
        postCommand("-exec-interrupt", Immediate);
810 811 812 813 814
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
        m_gdbAdapter->interruptInferior();
    }
con's avatar
con committed
815 816
}

817 818
void GdbEngine::interruptInferiorTemporarily()
{
819
    foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
820
        if (cmd.flags & LosesChild) {
hjk's avatar
hjk committed
821
            notifyInferiorIll();
822
            return;
823
        }
824
    }
825
    requestInterruptInferior();
826 827
}

con's avatar
con committed
828 829
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
830
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
831
    if (pid == 0) {
832
        showMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
833 834
        return;
    }
835
    if (pid == inferiorPid())
con's avatar
con committed