gdbengine.cpp 183 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 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"
37
#include "gdboptionspage.h"
con's avatar
con committed
38

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

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

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

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

hjk's avatar
hjk committed
83
using namespace Core;
hjk's avatar
hjk committed
84
using namespace ProjectExplorer;
85
using namespace Utils;
hjk's avatar
hjk committed
86

87 88
namespace Debugger {
namespace Internal {
con's avatar
con committed
89

90 91 92
enum { debugPending = 0 };

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

94 95
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

96

97
QByteArray GdbEngine::tooltipIName(const QString &exp)
98
{
99
    return "tooltip." + exp.toLatin1().toHex();
100 101
}

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

con's avatar
con committed
135 136 137 138 139 140
static int &currentToken()
{
    static int token = 0;
    return token;
}

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

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

class DebugInfoTask
{
public:
    QString command;
};

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

hjk's avatar
hjk committed
172
    bool canHandle(const Task &task) const
173 174 175 176 177 178 179 180 181 182 183 184 185 186
    {
        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
187
    QAction *createAction(QObject *parent) const
188
    {
hjk's avatar
hjk committed
189
        QAction *action = new QAction(DebuggerPlugin::tr("Install &Debug Information"), parent);
190
        action->setToolTip(DebuggerPlugin::tr("Tries to install missing debug information."));
191 192 193 194 195 196 197 198
        return action;
    }

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

con's avatar
con committed
199 200 201 202 203 204
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

205 206
GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters)
  : DebuggerEngine(startParameters)
con's avatar
con committed
207
{
208
    setObjectName(_("GdbEngine"));
209

210
    m_busy = false;
211
    m_gdbVersion = 100;
212
    m_isQnxGdb = false;
213 214 215
    m_registerNamesListed = false;
    m_sourcesListUpdating = false;
    m_oldestAcceptableToken = -1;
216
    m_nonDiscardableCount = 0;
217 218 219
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingBreakpointRequests = 0;
    m_commandsDoneCallback = 0;
220
    m_stackNeeded = false;
221
    m_preparedForQmlBreak = false;
222
    m_terminalTrap = startParameters.useTerminal;
223
    m_fullStartDone = false;
224
    m_systemDumpersLoaded = false;
225
    m_gdbProc = new GdbProcess(this);
226

227
    m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
hjk's avatar
hjk committed
228
    //ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler);
229

230 231 232
    m_commandTimer.setSingleShot(true);
    connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));

hjk's avatar
hjk committed
233
    connect(action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
234
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
235
    connect(action(CreateFullBacktrace), SIGNAL(triggered()),
236
            SLOT(createFullBacktrace()));
hjk's avatar
hjk committed
237
    connect(action(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
238
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
239
    connect(action(UseDynamicType), SIGNAL(valueChanged(QVariant)),
240
            SLOT(reloadLocals()));
ck's avatar
ck committed
241 242
}

con's avatar
con committed
243 244
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
245
    //ExtensionSystem::PluginManager::removeObject(m_debugInfoTaskHandler);
246 247 248
    delete m_debugInfoTaskHandler;
    m_debugInfoTaskHandler = 0;

249
    // Prevent sending error messages afterwards.
250 251 252 253 254 255
    disconnect();
}

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

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

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

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

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

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

340 341 342 343 344 345 346 347
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;
348 349 350
    const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
    const bool endFits = pos + size == s || message.at(pos + size) == '\n';
    return beginFits && endFits;
351 352
}

353 354 355 356 357 358 359 360
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
361 362 363 364 365 366 367 368

    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);
369 370
}

371
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
372
{
373
    showMessage(QString::fromLocal8Bit(buff, buff.length()), LogOutput);
con's avatar
con committed
374

375 376
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
377

378 379 380
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
381

382
    int token = -1;
383
    // Token is a sequence of numbers.
384 385
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
386
            break;
387 388 389 390
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
    }
con's avatar
con committed
391

392
    // Next char decides kind of response.
393 394 395 396 397 398 399 400 401 402 403 404
    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
405

hjk's avatar
hjk committed
406
            GdbMi result;
407 408
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
409
                if (*from != ',') {
410 411
                    // happens on archer where we get
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb)
hjk's avatar
hjk committed
412
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
413 414 415 416 417
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
418 419 420
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
421 422
                }
            }
423
            if (asyncClass == "stopped") {
424
                handleStopResponse(result);
425 426
                m_pendingLogStreamOutput.clear();
                m_pendingConsoleStreamOutput.clear();
427
            } else if (asyncClass == "running") {
428
                GdbMi threads = result["thread-id"];
hjk's avatar
hjk committed
429
                threadsHandler()->notifyRunning(threads.data());
430 431 432 433
                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()))));
hjk's avatar
hjk committed
434
                } else if (HostOsInfo::isWindowsHost() && (state() == InferiorStopRequested
435 436 437 438
                               || state() == InferiorShutdownRequested)) {
                    // FIXME: Breakpoints on Windows are exceptions which are thrown in newly
                    // created threads so we have to filter out the running threads messages when
                    // we request a stop.
439 440 441
                } else {
                    notifyInferiorRunOk();
                }
442 443 444 445
            } 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",
446
                // symbols-loaded="0"
hjk's avatar
hjk committed
447 448 449 450 451

                // 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"
452
                QByteArray id = result["id"].data();
453
                if (!id.isEmpty())
454
                    showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
455
                progressPing();
hjk's avatar
hjk committed
456 457 458
                Module module;
                module.startAddress = 0;
                module.endAddress = 0;
459 460
                module.hostPath = _(result["host-name"].data());
                module.modulePath = _(result["target-name"].data());
hjk's avatar
hjk committed
461
                module.moduleName = QFileInfo(module.hostPath).baseName();
462
                modulesHandler()->updateModule(module);
hjk's avatar
hjk committed
463 464 465 466
            } 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"
467
                QByteArray id = result["id"].data();
468
                progressPing();
469
                showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
470 471
            } else if (asyncClass == "thread-group-added") {
                // 7.1-symbianelf has "{id="i1"}"
472 473 474
            } 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
475 476
                // *-started seems to be standard in 7.1, but in early
                // 7.0.x, there was a *-created instead.
477
                progressPing();
ck's avatar
ck committed
478
                // 7.1.50 has thread-group-started,id="i1",pid="3529"
479
                QByteArray id = result["id"].data();
480
                showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
ck's avatar
ck committed
481 482
                int pid = id.toInt();
                if (!pid) {
483
                    id = result["pid"].data();
ck's avatar
ck committed
484 485 486 487
                    pid = id.toInt();
                }
                if (pid)
                    notifyInferiorPid(pid);
488
                handleThreadGroupCreated(result);
489
            } else if (asyncClass == "thread-created") {
490
                //"{id="1",group-id="28902"}"
491
                QByteArray id = result["id"].data();
492
                showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
hjk's avatar
hjk committed
493 494
                ThreadData thread;
                thread.id = ThreadId(id.toLong());
495
                thread.groupId = result["group-id"].data();
hjk's avatar
hjk committed
496
                threadsHandler()->updateThread(thread);
497
            } else if (asyncClass == "thread-group-exited") {
498
                // Archer has "{id="28902"}"
499
                QByteArray id = result["id"].data();
500
                showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
501
                handleThreadGroupExited(result);
502
            } else if (asyncClass == "thread-exited") {
503
                //"{id="1",group-id="28902"}"
504 505
                QByteArray id = result["id"].data();
                QByteArray groupid = result["group-id"].data();
506
                showStatusMessage(tr("Thread %1 in group %2 exited")
hjk's avatar
hjk committed
507
                    .arg(_(id)).arg(_(groupid)), 1000);
hjk's avatar
hjk committed
508
                threadsHandler()->removeThread(ThreadId(id.toLong()));
hjk's avatar
hjk committed
509
            } else if (asyncClass == "thread-selected") {
510
                QByteArray id = result["id"].data();
511
                showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
512
                //"{id="2"}"
513 514
            } else if (asyncClass == "breakpoint-modified") {
                // New in FSF gdb since 2011-04-27.
hjk's avatar
hjk committed
515 516 517 518 519 520
                // "{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",
521
                // fullname="/data/...line="135"},{number="3.2"...}}.."
hjk's avatar
hjk committed
522 523 524 525 526 527 528 529

                // 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
530
                ba.remove(pos1, pos3 - pos1 + 1);
hjk's avatar
hjk committed
531 532 533
                result = GdbMi();
                result.fromString(ba);
                BreakHandler *handler = breakHandler();
534
                BreakpointModelId id;
hjk's avatar
hjk committed
535 536
                BreakpointResponse br;
                foreach (const GdbMi &bkpt, result.children()) {
537
                    const QByteArray nr = bkpt["number"].data();
538
                    BreakpointResponseId rid(nr);
539
                    if (!isHiddenBreakpoint(rid)) {
540 541 542 543 544 545
                        if (nr.contains('.')) {
                            // A sub-breakpoint.
                            BreakpointResponse sub;
                            updateResponse(sub, bkpt);
                            sub.id = rid;
                            sub.type = br.type;
546
                            handler->insertSubBreakpoint(id, sub);
547 548
                        } else {
                            // A primary breakpoint.
549 550 551 552 553
                            id = handler->findBreakpointByResponseId(rid);
                            //qDebug() << "NR: " << nr << "RID: " << rid
                            //    << "ID: " << id;
                            //BreakpointModelId id =
                            //    handler->findBreakpointByResponseId(rid);
554 555
                            br = handler->response(id);
                            updateResponse(br, bkpt);
556
                            handler->setResponse(id, br);
557
                        }
hjk's avatar
hjk committed
558 559
                    }
                }
560 561 562
            } else if (asyncClass == "breakpoint-created") {
                // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
                //  addr="<PENDING>",pending="main",times="0",
563 564
                //  original-location="main"}}" -- or --
                // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
565
                // what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
566
                BreakHandler *handler = breakHandler();
567 568
                foreach (const GdbMi &bkpt, result.children()) {
                    BreakpointResponse br;
569
                    br.type = BreakpointByFileAndLine;
570
                    updateResponse(br, bkpt);
571
                    handler->handleAlienBreakpoint(br, this);
572 573 574 575 576
                }
            } else if (asyncClass == "breakpoint-deleted") {
                // "breakpoint-deleted" "{id="1"}"
                // New in FSF gdb since 2011-04-27.
                BreakHandler *handler = breakHandler();
577
                QByteArray nr = result["id"].data();
578 579
                BreakpointResponseId rid(nr);
                BreakpointModelId id = handler->findBreakpointByResponseId(rid);
580 581 582 583 584 585 586
                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);
                }
587 588 589
            } else if (asyncClass == "cmd-param-changed") {
                // New since 2012-08-09
                //  "{param="debug remote",value="1"}"
590 591 592
            } else if (asyncClass == "memory-changed") {
                // New since 2013
                //   "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
593
            } else {
594
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
595
                    << asyncClass << result.toString();
596
            }
597 598
            break;
        }
599

600
        case '~': {
601 602
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
603 604

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

            // Show some messages to give the impression something happens.
623
            if (data.startsWith("Reading symbols from ")) {
624
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
625
                progressPing();
626 627 628
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
629
                progressPing();
630
                showStatusMessage(_(data), 1000);
631 632 633 634
            } 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
635 636
                unsigned exCode;
                m_lastWinException = msgWinException(data, &exCode);
637
                showMessage(m_lastWinException, LogMisc);
638
                const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning;
639
                TaskHub::addTask(type, m_lastWinException, Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
640
            }
641 642 643 644

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

650 651
            break;
        }
652

653
        case '@': {
654
            readDebugeeOutput(GdbMi::parseCString(from, to));
655
            break;
con's avatar
con committed
656 657
        }

658 659 660 661 662 663
        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:"))
664
                showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
665

666
            if (isGdbConnectionError(data)) {
667
                notifyInferiorExited();
668
                break;
669
            }
670

hjk's avatar
hjk committed
671
            if (boolSetting(IdentifyDebugInfoPackages)) {
672 673 674 675 676 677 678 679 680 681
                // 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),
hjk's avatar
hjk committed
682
                        FileName(), 0, Debugger::Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO);
683

Nicolas Arnaud-Cormos's avatar
Nicolas Arnaud-Cormos committed
684
                    TaskHub::addTask(task);
685 686 687 688 689

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

con's avatar
con committed
692 693 694
            break;
        }

695
        case '^': {
hjk's avatar
hjk committed
696
            GdbResponse response;
con's avatar
con committed
697

hjk's avatar
hjk committed
698
            response.token = token;
con's avatar
con committed
699

700 701 702
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
703

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
704
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
705
            if (resultClass == "done")
hjk's avatar
hjk committed
706
                response.resultClass = GdbResultDone;
707
            else if (resultClass == "running")
hjk's avatar
hjk committed
708
                response.resultClass = GdbResultRunning;
709
            else if (resultClass == "connected")
hjk's avatar
hjk committed
710
                response.resultClass = GdbResultConnected;
711
            else if (resultClass == "error")
hjk's avatar
hjk committed
712
                response.resultClass = GdbResultError;
713
            else if (resultClass == "exit")
hjk's avatar
hjk committed
714
                response.resultClass = GdbResultExit;
715
            else
hjk's avatar
hjk committed
716
                response.resultClass = GdbResultUnknown;
con's avatar
con committed
717

718 719
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
720 721
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
722 723 724
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
725
                } else {
726
                    // Archer has this.
hjk's avatar
hjk committed
727 728
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
729 730
                }
            }
731 732 733

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
734 735
            response.logStreamOutput = m_pendingLogStreamOutput;
            response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
736 737 738
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

739
            handleResultRecord(&response);
740 741 742
            break;
        }
        default: {
743
            qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
744
            break;
con's avatar
con committed
745 746 747 748 749 750
        }
    }
}

void GdbEngine::readGdbStandardError()
{
751
    QByteArray err = m_gdbProc->readAllStandardError();
752
    showMessage(_("UNEXPECTED GDB STDERR: " + err));
753 754
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
755 756
    if (err.startsWith("BFD: reopening"))
        return;
Jarek Kobus's avatar
Jarek Kobus committed
757
    qWarning() << "Unexpected GDB stderr:" << err;
con's avatar
con committed
758 759 760 761
}

void GdbEngine::readGdbStandardOutput()
{
762
    m_commandTimer.start(); // Restart timer.
763

764 765
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
766

767
    QByteArray out = m_gdbProc->readAllStandardOutput();
hjk's avatar
hjk committed
768
    m_inbuffer.append(out);
con's avatar
con committed
769

770
    // This can trigger when a dialog starts a nested event loop.
771 772 773
    if (m_busy)
        return;

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

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

803
    if (usesExecInterrupt()) {
hjk's avatar
hjk committed
804
        postCommand("-exec-interrupt", Immediate);
805 806 807
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
hjk's avatar
hjk committed
808
        if (HostOsInfo::isWindowsHost() && !m_isQnxGdb) {
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
            QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); notifyInferiorStopFailed());
            QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed());
            m_signalOperation = startParameters().device->signalOperation();
            QTC_ASSERT(m_signalOperation, notifyInferiorStopFailed());
            connect(m_signalOperation.data(), SIGNAL(finished(QString)),
                    SLOT(handleInterruptDeviceInferior(QString)));

            m_signalOperation->setDebuggerCommand(startParameters().debuggerCommand);
            m_signalOperation->interruptProcess(inferiorPid());
        } else {
            interruptInferior2();
        }
    }
}

void GdbEngine::handleInterruptDeviceInferior(const QString &error)
{
    if (error.isEmpty()) {
        showMessage(QLatin1String("Interrupted ") + QString::number(inferiorPid()));
        notifyInferiorStopOk();
    } else {
        showMessage(error, LogError);
        notifyInferiorStopFailed();
832
    }
833 834
    m_signalOperation->disconnect(this);
    m_signalOperation.clear();
con's avatar
con committed
835 836
}

837 838
void GdbEngine::interruptInferiorTemporarily()
{
839
    foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
840
        if (cmd.flags & LosesChild) {
hjk's avatar
hjk committed
841
            notifyInferiorIll();
842
            return;
843
        }
844
    }
845
    requestInterruptInferior();
846 847
}

con's avatar
con committed
848 849
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
850
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
851
    if (pid == 0) {
852
        showMessage(