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
** 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
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** 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
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
con's avatar
con committed
30 31

#include "gdbengine.h"
32

33 34
#include "attachgdbadapter.h"
#include "coregdbadapter.h"
35
#include "gdbplainengine.h"
36
#include "termgdbadapter.h"
ck's avatar
ck committed
37
#include "remotegdbserveradapter.h"
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
#include <projectexplorer/devicesupport/deviceprocess.h>
69
#include <projectexplorer/itaskhandler.h>
70
#include <projectexplorer/taskhub.h>
71
#include <texteditor/texteditor.h>
72
#include <utils/algorithm.h>
73
#include <utils/hostosinfo.h>
74 75
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
hjk's avatar
hjk committed
76
#include <utils/savedaction.h>
hjk's avatar
hjk committed
77

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

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

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

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

91 92 93
enum { debugPending = 0 };

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

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

97

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

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

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

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

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

class DebugInfoTask
{
public:
    QString command;
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

651 652
            break;
        }
653

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

804
    if (usesExecInterrupt()) {
hjk's avatar
hjk committed
805
        postCommand("-exec-interrupt", Immediate);
806 807 808
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
hjk's avatar
hjk committed
809
        if (HostOsInfo::isWindowsHost() && !m_isQnxGdb) {
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
            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();
833
    }
834 835
    m_signalOperation->disconnect(this);
    m_signalOperation.clear();
con's avatar
con committed
836 837
}

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

con's avatar
con committed
849 850
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
851
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
852
    if (pid == 0) {
853
        showMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
854 855
        return;
    }
856
    if (pid == inferiorPid())
con's avatar
con committed
857
        return;
858

859 860
    showMessage(_("FOUND PID %1").arg(pid));
    notifyInferiorPid(pid);
con's avatar
con committed
861 862
}

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

869
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
870 871
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
872 873 874 875 876 877 878 879 880 881 882
{
    GdbCommand cmd;
    cmd.command = command;
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

void GdbEngine::postCommandHelper(const GdbCommand &cm