gdbengine.cpp 199 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
con's avatar
con committed
29 30

#include "gdbengine.h"
31

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

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

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggerinternalconstants.h>
#include <debugger/debuggerruncontrolfactory.h>
#include <debugger/disassemblerlines.h>

#include <debugger/debuggeractions.h>
#include <debugger/debuggercore.h>
#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerplugin.h>
#include <debugger/debuggerprotocol.h>
#include <debugger/debuggerstringutils.h>
#include <debugger/debuggertooltipmanager.h>
#include <debugger/disassembleragent.h>
#include <debugger/memoryagent.h>
#include <debugger/sourceutils.h>

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

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

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

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

hjk's avatar
hjk committed
82
using namespace ProjectExplorer;
83
using namespace Utils;
hjk's avatar
hjk committed
84

85 86
namespace Debugger {
namespace Internal {
con's avatar
con committed
87

88 89 90
enum { debugPending = 0 };

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

92 93
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

94
QByteArray GdbEngine::tooltipIName(const QString &exp)
95
{
96
    return "tooltip." + exp.toLatin1().toHex();
97 98
}

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

con's avatar
con committed
132 133 134 135 136 137
static int &currentToken()
{
    static int token = 0;
    return token;
}

hjk's avatar
hjk committed
138
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
139
{
140
    QByteArray out = response.consoleStreamOutput;
141 142 143 144 145 146 147 148 149
    // 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);
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
///////////////////////////////////////////////////////////////////////
//
// Debuginfo Taskhandler
//
///////////////////////////////////////////////////////////////////////

class DebugInfoTask
{
public:
    QString command;
};

class DebugInfoTaskHandler : public  ProjectExplorer::ITaskHandler
{
public:
hjk's avatar
hjk committed
165 166
    explicit DebugInfoTaskHandler(GdbEngine *engine)
        : m_engine(engine)
167 168
    {}

hjk's avatar
hjk committed
169
    bool canHandle(const Task &task) const
170 171 172 173 174 175 176 177 178 179 180 181 182 183
    {
        return m_debugInfoTasks.contains(task.taskId);
    }

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

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

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

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

con's avatar
con committed
196 197 198 199 200 201
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

202 203
GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters)
  : DebuggerEngine(startParameters)
con's avatar
con committed
204
{
205
    setObjectName(_("GdbEngine"));
206

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

232 233
    invalidateSourcesList();

234
    m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
hjk's avatar
hjk committed
235
    //ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler);
236

237 238 239
    m_commandTimer.setSingleShot(true);
    connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));

hjk's avatar
hjk committed
240
    connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
241
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
242
    connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
243
            SLOT(createFullBacktrace()));
244 245 246 247
    connect(debuggerCore()->action(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
    connect(debuggerCore()->action(UseDynamicType), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
248 249
    connect(debuggerCore()->action(IntelFlavor), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadDisassembly()));
ck's avatar
ck committed
250 251
}

con's avatar
con committed
252 253
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
254
    //ExtensionSystem::PluginManager::removeObject(m_debugInfoTaskHandler);
255 256 257
    delete m_debugInfoTaskHandler;
    m_debugInfoTaskHandler = 0;

258
    // Prevent sending error messages afterwards.
259 260 261 262 263 264
    disconnect();
}

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

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

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
301
    Q_UNUSED(to)
con's avatar
con committed
302 303 304 305 306 307 308 309 310 311 312
    // 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

313 314
// Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
// and return an exception message
315
static inline QString msgWinException(const QByteArray &data, unsigned *exCodeIn = 0)
316
{
317 318
    if (exCodeIn)
        *exCodeIn = 0;
319 320 321 322 323 324
    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);
325 326
    if (exCodeIn)
        *exCodeIn = exCode;
327 328 329
    const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
    QString rc;
    QTextStream str(&rc);
330
    str << GdbEngine::tr("An exception was triggered:") << ' ';
331 332 333 334 335
    formatWindowsException(exCode, address, 0, 0, 0, str);
    str << '.';
    return rc;
}

336 337
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
338 339
    QString msg = m_outputCodec->toUnicode(data.constData(), data.length(),
        &m_outputCodecState);
340
    showMessage(msg, AppOutput);
341 342
}

hjk's avatar
hjk committed
343 344 345 346 347 348
static bool isNameChar(char c)
{
    // could be 'stopped' or 'shlibs-added'
    return (c >= 'a' && c <= 'z') || c == '-';
}

349 350 351 352 353 354 355 356
static bool contains(const QByteArray &message, const char *pattern, int size)
{
    const int s = message.size();
    if (s < size)
        return false;
    const int pos = message.indexOf(pattern);
    if (pos == -1)
        return false;
357 358 359
    const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
    const bool endFits = pos + size == s || message.at(pos + size) == '\n';
    return beginFits && endFits;
360 361
}

362 363 364 365 366 367 368 369
static bool isGdbConnectionError(const QByteArray &message)
{
    // Handle messages gdb client produces when the target exits (gdbserver)
    //
    // we get this as response either to a specific command, e.g.
    //    31^error,msg="Remote connection closed"
    // or as informative output:
    //    &Remote connection closed
370 371 372 373 374 375 376 377

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

    return contains(message, msg1, sizeof(msg1) - 1)
        || contains(message, msg2, sizeof(msg2) - 1)
        || contains(message, msg3, sizeof(msg3) - 1);
378 379
}

380
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
381
{
382
    showMessage(QString::fromLocal8Bit(buff, buff.length()), LogOutput);
con's avatar
con committed
383

384 385
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
386

387 388 389
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
390

391
    int token = -1;
392
    // Token is a sequence of numbers.
393 394
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
395
            break;
396 397 398 399
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
    }
con's avatar
con committed
400

401
    // Next char decides kind of response.
402 403 404 405 406 407 408 409 410 411 412 413
    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
414

hjk's avatar
hjk committed
415
            GdbMi result;
416 417
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
418
                if (*from != ',') {
419 420
                    // happens on archer where we get
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb)
hjk's avatar
hjk committed
421
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
422 423 424 425 426
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
427 428 429
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
430 431
                }
            }
432
            if (asyncClass == "stopped") {
433
                handleStopResponse(result);
434 435
                m_pendingLogStreamOutput.clear();
                m_pendingConsoleStreamOutput.clear();
436
            } else if (asyncClass == "running") {
437
                GdbMi threads = result["thread-id"];
hjk's avatar
hjk committed
438
                threadsHandler()->notifyRunning(threads.data());
439 440 441 442
                if (state() == InferiorRunOk || state() == InferiorSetupRequested) {
                    // We get multiple *running after thread creation and in Windows terminals.
                    showMessage(QString::fromLatin1("NOTE: INFERIOR STILL RUNNING IN STATE %1.").
                                arg(QLatin1String(DebuggerEngine::stateName(state()))));
443 444 445
                } else {
                    notifyInferiorRunOk();
                }
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 452 453 454 455

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

634
        case '~': {
635 636
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
637 638

            // Parse pid from noise.
639
            if (!inferiorPid()) {
640 641 642 643
                // 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
644 645 646
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
647 648 649 650 651
                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));
652 653
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
654
            }
655 656

            // Show some messages to give the impression something happens.
657
            if (data.startsWith("Reading symbols from ")) {
658
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
659
                progressPing();
660
                invalidateSourcesList();
661 662 663
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
664
                progressPing();
665
                showStatusMessage(_(data), 1000);
666 667 668 669
            } 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
670 671
                unsigned exCode;
                m_lastWinException = msgWinException(data, &exCode);
672
                showMessage(m_lastWinException, LogMisc);
673
                const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning;
674
                TaskHub::addTask(type, m_lastWinException, Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
675
            }
676 677 678 679

            if (data.startsWith("QMLBP:")) {
                int pos1 = 6;
                int pos2 = data.indexOf(' ', pos1);
680
                m_qmlBreakpointResponseId2 = BreakpointResponseId(data.mid(pos1, pos2 - pos1));
681 682 683 684
                //qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
                //qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
            }

685 686
            break;
        }
687

688
        case '@': {
689
            readDebugeeOutput(GdbMi::parseCString(from, to));
690
            break;
con's avatar
con committed
691 692
        }

693 694 695 696 697 698
        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:"))
699
                showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
700

701
            if (isGdbConnectionError(data)) {
702
                notifyInferiorExited();
703
                break;
704
            }
705

706 707 708 709 710 711 712 713 714 715 716 717 718
            if (debuggerCore()->boolSetting(IdentifyDebugInfoPackages)) {
                // From SuSE's gdb: >&"Missing separate debuginfo for ...\n"
                // ">&"Try: zypper install -C \"debuginfo(build-id)=c084ee5876ed1ac12730181c9f07c3e027d8e943\"\n"
                if (data.startsWith("Missing separate debuginfo for ")) {
                    m_lastMissingDebugInfo = QString::fromLocal8Bit(data.mid(32));
                } else if (data.startsWith("Try: zypper")) {
                    QString cmd = QString::fromLocal8Bit(data.mid(4));

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

Nicolas Arnaud-Cormos's avatar
Nicolas Arnaud-Cormos committed
719
                    TaskHub::addTask(task);
720 721 722 723 724

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

con's avatar
con committed
727 728 729
            break;
        }

730
        case '^': {
hjk's avatar
hjk committed
731
            GdbResponse response;
con's avatar
con committed
732

hjk's avatar
hjk committed
733
            response.token = token;
con's avatar
con committed
734

735 736 737
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
738

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
739
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
740
            if (resultClass == "done")
hjk's avatar
hjk committed
741
                response.resultClass = GdbResultDone;
742
            else if (resultClass == "running")
hjk's avatar
hjk committed
743
                response.resultClass = GdbResultRunning;
744
            else if (resultClass == "connected")
hjk's avatar
hjk committed
745
                response.resultClass = GdbResultConnected;
746
            else if (resultClass == "error")
hjk's avatar
hjk committed
747
                response.resultClass = GdbResultError;
748
            else if (resultClass == "exit")
hjk's avatar
hjk committed
749
                response.resultClass = GdbResultExit;
750
            else
hjk's avatar
hjk committed
751
                response.resultClass = GdbResultUnknown;
con's avatar
con committed
752

753 754
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
755 756
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
757 758 759
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
760
                } else {
761
                    // Archer has this.
hjk's avatar
hjk committed
762 763
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
764 765
                }
            }
766 767 768

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
769 770
            response.logStreamOutput = m_pendingLogStreamOutput;
            response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
771 772 773
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

774
            handleResultRecord(&response);
775 776 777
            break;
        }
        default: {
778
            qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
779
            break;
con's avatar
con committed
780 781 782 783 784 785
        }
    }
}

void GdbEngine::readGdbStandardError()
{
786
    QByteArray err = m_gdbProc->readAllStandardError();
787
    showMessage(_("UNEXPECTED GDB STDERR: " + err));
788 789
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
790 791
    if (err.startsWith("BFD: reopening"))
        return;
Jarek Kobus's avatar
Jarek Kobus committed
792
    qWarning() << "Unexpected GDB stderr:" << err;
con's avatar
con committed
793 794 795 796
}

void GdbEngine::readGdbStandardOutput()
{
797
    m_commandTimer.start(); // Restart timer.
798

799 800
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
801

802
    QByteArray out = m_gdbProc->readAllStandardOutput();
hjk's avatar
hjk committed
803
    m_inbuffer.append(out);
con's avatar
con committed
804

805
    // This can trigger when a dialog starts a nested event loop.
806 807 808
    if (m_busy)
        return;

809 810
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
811
        int end = m_inbuffer.indexOf('\n', scan);
812 813 814 815 816
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
817
        scan = newstart;
818 819 820 821 822 823 824
        if (end == start)
            continue;
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
825
        m_busy = true;
hjk's avatar
hjk committed
826 827
        QByteArray ba = QByteArray::fromRawData(m_inbuffer.constData() + start, end - start);
        handleResponse(ba);
828
        m_busy = false;
con's avatar
con committed
829
    }
830
    m_inbuffer.clear();
con's avatar
con committed
831 832 833 834
}

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

838
    if (usesExecInterrupt()) {
hjk's avatar
hjk committed
839
        postCommand("-exec-interrupt", Immediate);
840 841 842
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
843
        interruptInferior2();
844
    }
con's avatar
con committed
845 846
}

847 848
void GdbEngine::interruptInferiorTemporarily()
{
849
    foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
850
        if (cmd.flags & LosesChild) {
hjk's avatar
hjk committed
851
            notifyInferiorIll();
852
            return;
853
        }
854
    }
855
    requestInterruptInferior();
856 857
}

con's avatar
con committed
858 859
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
860
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
861
    if (pid == 0) {
862
        showMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
863 864
        return;
    }
865
    if (pid == inferiorPid())
con's avatar
con committed
866
        return;
867

868 869
    showMessage(_("FOUND PID %1").arg(pid));
    notifyInferiorPid(pid);
con's avatar
con committed
870 871
}

872
void GdbEngine::postCommand(const QByteArray &command, GdbCommandCallback callback,
873 874
                            const char *callbackName, const QVariant &cookie)
{
875
    postCommand(command, NoFlags, callback, callbackName, cookie);
876 877
}

878
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,