gdbengine.cpp 184 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/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 Core;
hjk's avatar
hjk committed
83
using namespace ProjectExplorer;
84
using namespace Utils;
hjk's avatar
hjk committed
85

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

89 90 91
enum { debugPending = 0 };

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

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

95

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

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

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

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

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

class DebugInfoTask
{
public:
    QString command;
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

649 650
            break;
        }
651

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

void GdbEngine::postCommandHelper(const GdbCommand &cmd)
con's avatar
con committed
881
{
hjk's avatar
hjk committed
882
    if (!stateAcceptsGdbCommands(state())) {
883
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.command));
884
        showMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
885
            .arg(_(cmd.command)).arg(state()));
con's avatar
con committed
886 887 888
        return;
    }

hjk's avatar