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 ProjectExplorer;
83
using namespace Utils;
hjk's avatar
hjk committed
84

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

88 89 90
enum { debugPending = 0 };

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

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

94

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

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

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

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

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

class DebugInfoTask
{
public:
    QString command;
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

648 649
            break;
        }
650

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

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

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

669 670 671 672 673 674 675 676 677 678 679 680 681
            if (debuggerCore()->boolSetting(IdentifyDebugInfoPackages)) {
                // From SuSE's gdb: >&"Missing separate debuginfo for ...\n"
                // ">&"Try: zypper install -C \"debuginfo(build-id)=c084ee5876ed1ac12730181c9f07c3e027d8e943\"\n"
                if (data.startsWith("Missing separate debuginfo for ")) {
                    m_lastMissingDebugInfo = QString::fromLocal8Bit(data.mid(32));
                } else if (data.startsWith("Try: zypper")) {
                    QString cmd = QString::fromLocal8Bit(data.mid(4));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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