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

#include "gdbengine.h"
31

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

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

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

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

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

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

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

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

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

233 234
    invalidateSourcesList();

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

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

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

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

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

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

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

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

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

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

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

350 351 352 353 354 355 356 357
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;
358 359 360
    const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
    const bool endFits = pos + size == s || message.at(pos + size) == '\n';
    return beginFits && endFits;
361 362
}

363 364 365 366 367 368 369 370
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
371 372 373 374 375 376 377 378

    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);
379 380
}

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

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

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

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

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

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

                // 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"
457
                QByteArray id = result["id"].data();
458
                if (!id.isEmpty())
459
                    showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
460
                progressPing();
461
                invalidateSourcesList();
hjk's avatar
hjk committed
462 463 464
                Module module;
                module.startAddress = 0;
                module.endAddress = 0;
465 466
                module.hostPath = _(result["host-name"].data());
                module.modulePath = _(result["target-name"].data());
hjk's avatar
hjk committed
467
                module.moduleName = QFileInfo(module.hostPath).baseName();
468
                modulesHandler()->updateModule(module);
hjk's avatar
hjk committed
469 470 471 472
            } 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"
473
                QByteArray id = result["id"].data();
474
                progressPing();
475
                showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
476
                invalidateSourcesList();
477 478
            } else if (asyncClass == "thread-group-added") {
                // 7.1-symbianelf has "{id="i1"}"
479 480 481
            } 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
482 483
                // *-started seems to be standard in 7.1, but in early
                // 7.0.x, there was a *-created instead.
484
                progressPing();
ck's avatar
ck committed
485
                // 7.1.50 has thread-group-started,id="i1",pid="3529"
486
                QByteArray id = result["id"].data();
487
                showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
ck's avatar
ck committed
488 489
                int pid = id.toInt();
                if (!pid) {
490
                    id = result["pid"].data();
ck's avatar
ck committed
491 492 493 494
                    pid = id.toInt();
                }
                if (pid)
                    notifyInferiorPid(pid);
495
                handleThreadGroupCreated(result);
496
            } else if (asyncClass == "thread-created") {
497
                //"{id="1",group-id="28902"}"
498
                QByteArray id = result["id"].data();
499
                showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
hjk's avatar
hjk committed
500 501
                ThreadData thread;
                thread.id = ThreadId(id.toLong());
502
                thread.groupId = result["group-id"].data();
hjk's avatar
hjk committed
503
                threadsHandler()->updateThread(thread);
504
            } else if (asyncClass == "thread-group-exited") {
505
                // Archer has "{id="28902"}"
506
                QByteArray id = result["id"].data();
507
                showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
508
                handleThreadGroupExited(result);
509
            } else if (asyncClass == "thread-exited") {
510
                //"{id="1",group-id="28902"}"
511 512
                QByteArray id = result["id"].data();
                QByteArray groupid = result["group-id"].data();
513
                showStatusMessage(tr("Thread %1 in group %2 exited")
hjk's avatar
hjk committed
514
                    .arg(_(id)).arg(_(groupid)), 1000);
hjk's avatar
hjk committed
515
                threadsHandler()->removeThread(ThreadId(id.toLong()));
hjk's avatar
hjk committed
516
            } else if (asyncClass == "thread-selected") {
517
                QByteArray id = result["id"].data();
518
                showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
519
                //"{id="2"}"
520 521
            } else if (m_isMacGdb && asyncClass == "shlibs-updated") {
                // Apple's gdb announces updated libs.
522
                invalidateSourcesList();
523 524
            } else if (m_isMacGdb && asyncClass == "shlibs-added") {
                // Apple's gdb announces added libs.
525 526 527 528 529
                // {shlib-info={num="2", name="libmathCommon.A_debug.dylib",
                // kind="-", dyld-addr="0x7f000", reason="dyld", requested-state="Y",
                // state="Y", path="/usr/lib/system/libmathCommon.A_debug.dylib",
                // description="/usr/lib/system/libmathCommon.A_debug.dylib",
                // loaded_addr="0x7f000", slide="0x7f000", prefix=""}}
530
                invalidateSourcesList();
531 532 533 534 535
            } else if (m_isMacGdb && asyncClass == "resolve-pending-breakpoint") {
                // Apple's gdb announces resolved breakpoints.
                // new_bp="1",pended_bp="1",new_expr="\"gdbengine.cpp\":1584",
                // bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
                // addr="0x0000000115cc3ddf",func="foo()",file="../foo.cpp",
536
                // line="1584",shlib="/../libFoo_debug.dylib",times="0"}
537 538
                const GdbMi bkpt = result["bkpt"];
                const BreakpointResponseId rid(bkpt["number"].data());
539
                if (!isQmlStepBreakpoint(rid)) {
hjk's avatar
hjk committed
540
                    BreakHandler *handler = breakHandler();
541
                    BreakpointModelId id = handler->findBreakpointByResponseId(rid);
hjk's avatar
hjk committed
542 543 544
                    BreakpointResponse br = handler->response(id);
                    updateResponse(br, bkpt);
                    handler->setResponse(id, br);
545
                    attemptAdjustBreakpointLocation(id);
546
                }
547 548
            } else if (asyncClass == "breakpoint-modified") {
                // New in FSF gdb since 2011-04-27.
hjk's avatar
hjk committed
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
                // "{bkpt={number="3",type="breakpoint",disp="keep",
                // enabled="y",addr="<MULTIPLE>",times="1",
                // original-location="\\",simple_gdbtest_app.cpp\\":135"},
                // {number="3.1",enabled="y",addr="0x0805ff68",
                // func="Vector<int>::Vector(int)",
                // file="simple_gdbtest_app.cpp",
                // fullname="/data/...line="135"},{number="3.2"...}}"

                // Note the leading comma in original-location. Filter it out.
                // We don't need the field anyway.
                QByteArray ba = result.toString();
                ba = '[' + ba.mid(6, ba.size() - 7) + ']';
                const int pos1 = ba.indexOf(",original-location");
                const int pos2 = ba.indexOf("\":", pos1 + 2);
                const int pos3 = ba.indexOf('"', pos2 + 2);
Robert Loehning's avatar
Robert Loehning committed
564
                ba.remove(pos1, pos3 - pos1 + 1);
hjk's avatar
hjk committed
565 566 567
                result = GdbMi();
                result.fromString(ba);
                BreakHandler *handler = breakHandler();
568
                BreakpointModelId id;
hjk's avatar
hjk committed
569 570
                BreakpointResponse br;
                foreach (const GdbMi &bkpt, result.children()) {
571
                    const QByteArray nr = bkpt["number"].data();
572
                    BreakpointResponseId rid(nr);
573
                    if (!isHiddenBreakpoint(rid)) {
574 575 576 577 578 579
                        if (nr.contains('.')) {
                            // A sub-breakpoint.
                            BreakpointResponse sub;
                            updateResponse(sub, bkpt);
                            sub.id = rid;
                            sub.type = br.type;
580
                            handler->insertSubBreakpoint(id, sub);
581 582
                        } else {
                            // A primary breakpoint.
583 584 585 586 587
                            id = handler->findBreakpointByResponseId(rid);
                            //qDebug() << "NR: " << nr << "RID: " << rid
                            //    << "ID: " << id;
                            //BreakpointModelId id =
                            //    handler->findBreakpointByResponseId(rid);
588 589
                            br = handler->response(id);
                            updateResponse(br, bkpt);
590
                            handler->setResponse(id, br);
591
                        }
hjk's avatar
hjk committed
592 593
                    }
                }
594
                m_hasBreakpointNotifications = true;
595 596 597
            } else if (asyncClass == "breakpoint-created") {
                // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
                //  addr="<PENDING>",pending="main",times="0",
598 599 600
                //  original-location="main"}}" -- or --
                // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
                //  what="*0xbfffed48",times="0",original-location="*0xbfffed48"
601
                BreakHandler *handler = breakHandler();
602 603
                foreach (const GdbMi &bkpt, result.children()) {
                    BreakpointResponse br;
604
                    br.type = BreakpointByFileAndLine;
605
                    updateResponse(br, bkpt);
606
                    handler->handleAlienBreakpoint(br, this);
607 608 609 610 611
                }
            } else if (asyncClass == "breakpoint-deleted") {
                // "breakpoint-deleted" "{id="1"}"
                // New in FSF gdb since 2011-04-27.
                BreakHandler *handler = breakHandler();
612
                QByteArray nr = result["id"].data();
613 614
                BreakpointResponseId rid(nr);
                BreakpointModelId id = handler->findBreakpointByResponseId(rid);
615 616 617 618 619 620 621
                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);
                }
622 623 624
            } else if (asyncClass == "cmd-param-changed") {
                // New since 2012-08-09
                //  "{param="debug remote",value="1"}"
625 626 627
            } else if (asyncClass == "memory-changed") {
                // New since 2013
                //   "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
628
            } else {
629
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
630
                    << asyncClass << result.toString();
631
            }
632 633
            break;
        }
634

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

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

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

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

686 687
            break;
        }
688

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

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

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

707 708 709 710 711 712 713 714 715 716 717 718 719
            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
720
                    TaskHub::addTask(task);
721 722 723 724 725

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

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

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

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

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

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

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

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

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

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

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

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

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

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

810 811
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
812
        int end = m_inbuffer.indexOf('\n', scan);
813 814 815 816 817
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
818
        scan = newstart;
819 820 821 822 823 824 825
        if (end == start)
            continue;
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
826
        m_busy = true;
hjk's avatar
hjk committed