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 221 222 223 224 225 226 227 228 229 230
///////////////////////////////////////////////////////////////////////
//
// 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)
    {
        QAction *action = new QAction(tr("Install &Debug Information"), parent);
        action->setToolTip(tr("This tries to install missing debug information."));
        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

266 267
    invalidateSourcesList();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

654 655
            break;
        }
656

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

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

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

            // 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
697 698 699
            break;
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

837 838
    showMessage(_("FOUND PID %1").arg(pid));
    notifyInferiorPid(pid);
con's avatar
con committed
839 840
}

841
void GdbEngine::postCommand(const QByteArray &command, AdapterCallback callback,
hjk's avatar
hjk committed
842
                            const char *callbackName, const QVariant &cookie)
843 844 845 846
{
    postCommand(command, NoFlags, callback, callbackName, cookie);
}

847
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
848 849
                            AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
hjk's avatar
hjk committed
850 851 852
{
    GdbCommand cmd;
    cmd.command = command;
853
    cmd.flags = flags;
hjk's avatar
hjk committed
854 855 856 857 858 859
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

860
void GdbEngine::postCommand(const QByteArray &command, GdbCommandCallback callback,
861 862
                            const char *callbackName, const QVariant &cookie)
{
863
    postCommand(command, NoFlags, callback, callbackName, cookie);
864 865
}

866
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
867 868
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
869 870 871 872 873 874 875 876 877 878 879
{
    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
880
{
hjk's avatar
hjk committed
881
    if (!stateAcceptsGdbCommands(state())) {
882
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.command));
883
        showMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
884
            .arg(_(cmd.command)).arg(state()));
con's avatar
con committed
885 886 887
        return;
    }

888 889
    if (cmd.flags & RebuildWatchModel) {
        ++m_pendingWatchRequests;
890
        PENDING_DEBUG("   WATCH MODEL:" << cmd.command << "=>" << cmd.callbackName
891
                      << "INCREMENTS PENDING TO" << m_pendingWatchRequests);
892 893 894 895
    } 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
896
    } else {
897
        PENDING_DEBUG("   OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
898