gdbengine.cpp 199 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"
ck's avatar
ck committed
34
#include "localplaingdbadapter.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 69
#include <projectexplorer/taskhub.h>
#include <projectexplorer/itaskhandler.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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
82
#ifdef Q_OS_UNIX
con's avatar
con committed
83 84 85
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
86
#include <ctype.h>
con's avatar
con committed
87

hjk's avatar
hjk committed
88
using namespace ProjectExplorer;
89
using namespace Utils;
hjk's avatar
hjk committed
90

91 92
namespace Debugger {
namespace Internal {
con's avatar
con committed
93

94 95 96
class GdbToolTipContext : public DebuggerToolTipContext
{
public:
97 98
    GdbToolTipContext(const DebuggerToolTipContext &c) :
        DebuggerToolTipContext(c), editor(0) {}
99 100 101

    QPoint mousePosition;
    QString expression;
102
    QByteArray iname;
103
    Core::IEditor *editor;
104 105
};

106 107 108
enum { debugPending = 0 };

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

110 111
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

112
QByteArray GdbEngine::tooltipIName(const QString &exp)
113
{
114
    return "tooltip." + exp.toLatin1().toHex();
115 116
}

hjk's avatar
hjk committed
117
static bool stateAcceptsGdbCommands(DebuggerState state)
hjk's avatar
hjk committed
118
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
119
    switch (state) {
hjk's avatar
hjk committed
120
    case EngineSetupRequested:
121 122
    case EngineSetupOk:
    case EngineSetupFailed:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
123
    case InferiorUnrunnable:
hjk's avatar
hjk committed
124
    case InferiorSetupRequested:
hjk's avatar
hjk committed
125
    case InferiorSetupFailed:
hjk's avatar
hjk committed
126 127 128 129 130 131 132 133
    case EngineRunRequested:
    case InferiorRunRequested:
    case InferiorRunOk:
    case InferiorStopRequested:
    case InferiorStopOk:
    case InferiorShutdownRequested:
    case EngineShutdownRequested:
    case InferiorShutdownOk:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
134 135 136 137
    case InferiorShutdownFailed:
        return true;
    case DebuggerNotReady:
    case InferiorStopFailed:
138
    case InferiorSetupOk:
hjk's avatar
hjk committed
139
    case EngineRunFailed:
hjk's avatar
hjk committed
140
    case InferiorExitOk:
hjk's avatar
hjk committed
141 142 143 144
    case InferiorRunFailed:
    case EngineShutdownOk:
    case EngineShutdownFailed:
    case DebuggerFinished:
145
        return false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
146 147 148
    }
    return false;
}
hjk's avatar
hjk committed
149

con's avatar
con committed
150 151 152 153 154 155
static int &currentToken()
{
    static int token = 0;
    return token;
}

hjk's avatar
hjk committed
156
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
157
{
158
    QByteArray out = response.consoleStreamOutput;
159 160 161 162 163 164 165 166 167
    // 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);
}

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
///////////////////////////////////////////////////////////////////////
//
// Debuginfo Taskhandler
//
///////////////////////////////////////////////////////////////////////

class DebugInfoTask
{
public:
    QString command;
};

class DebugInfoTaskHandler : public  ProjectExplorer::ITaskHandler
{
public:
hjk's avatar
hjk committed
183 184
    explicit DebugInfoTaskHandler(GdbEngine *engine)
        : m_engine(engine)
185 186
    {}

hjk's avatar
hjk committed
187
    bool canHandle(const Task &task) const
188 189 190 191 192 193 194 195 196 197 198 199 200 201
    {
        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
202
    QAction *createAction(QObject *parent) const
203
    {
hjk's avatar
hjk committed
204
        QAction *action = new QAction(DebuggerPlugin::tr("Install &Debug Information"), parent);
205
        action->setToolTip(DebuggerPlugin::tr("Tries to install missing debug information."));
206 207 208 209 210 211 212 213
        return action;
    }

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

con's avatar
con committed
214 215 216 217 218 219
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

220 221
GdbEngine::GdbEngine(const DebuggerStartParameters &startParameters)
  : DebuggerEngine(startParameters)
con's avatar
con committed
222
{
223
    setObjectName(_("GdbEngine"));
224

225
    m_busy = false;
226 227 228 229
    m_debuggingHelperState = DebuggingHelperUninitialized;
    m_gdbVersion = 100;
    m_gdbBuildVersion = -1;
    m_isMacGdb = false;
230
    m_isQnxGdb = false;
231
    m_hasBreakpointNotifications = false;
232 233 234 235
    m_hasPython = false;
    m_registerNamesListed = false;
    m_sourcesListUpdating = false;
    m_oldestAcceptableToken = -1;
236
    m_nonDiscardableCount = 0;
237 238 239
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingBreakpointRequests = 0;
    m_commandsDoneCallback = 0;
240
    m_stackNeeded = false;
241
    m_preparedForQmlBreak = false;
242
    m_disassembleUsesComma = false;
243
    m_terminalTrap = startParameters.useTerminal;
244
    m_fullStartDone = false;
245
    m_systemDumpersLoaded = false;
hjk's avatar
hjk committed
246
    m_forceAsyncModel = false;
247
    m_pythonAttemptedToLoad = false;
248

249 250
    invalidateSourcesList();

251
    m_debugInfoTaskHandler = new DebugInfoTaskHandler(this);
hjk's avatar
hjk committed
252
    //ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler);
253

254 255 256
    m_commandTimer.setSingleShot(true);
    connect(&m_commandTimer, SIGNAL(timeout()), SLOT(commandTimeout()));

hjk's avatar
hjk committed
257
    connect(debuggerCore()->action(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
258
            SLOT(reloadLocals()));
hjk's avatar
hjk committed
259
    connect(debuggerCore()->action(CreateFullBacktrace), SIGNAL(triggered()),
260
            SLOT(createFullBacktrace()));
261 262 263 264
    connect(debuggerCore()->action(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
    connect(debuggerCore()->action(UseDynamicType), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadLocals()));
265 266
    connect(debuggerCore()->action(IntelFlavor), SIGNAL(valueChanged(QVariant)),
            SLOT(reloadDisassembly()));
ck's avatar
ck committed
267 268
}

con's avatar
con committed
269 270
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
271
    //ExtensionSystem::PluginManager::removeObject(m_debugInfoTaskHandler);
272 273 274
    delete m_debugInfoTaskHandler;
    m_debugInfoTaskHandler = 0;

275
    // Prevent sending error messages afterwards.
276 277 278 279 280 281
    disconnect();
}

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

284
QString GdbEngine::errorMessage(QProcess::ProcessError error)
con's avatar
con committed
285 286 287
{
    switch (error) {
        case QProcess::FailedToStart:
Jarek Kobus's avatar
Jarek Kobus committed
288
            return tr("The gdb process failed to start. Either the "
289
                "invoked program \"%1\" is missing, or you may have insufficient "
290 291
                "permissions to invoke the program.\n%2")
                .arg(m_gdb, gdbProc()->errorString());
con's avatar
con committed
292
        case QProcess::Crashed:
293 294 295 296 297
            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
298
        case QProcess::Timedout:
299
            return tr("The last waitFor...() function timed out. "
con's avatar
con committed
300 301 302
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
        case QProcess::WriteError:
303
            return tr("An error occurred when attempting to write "
Jarek Kobus's avatar
Jarek Kobus committed
304
                "to the gdb process. For example, the process may not be running, "
con's avatar
con committed
305 306
                "or it may have closed its input channel.");
        case QProcess::ReadError:
307
            return tr("An error occurred when attempting to read from "
Jarek Kobus's avatar
Jarek Kobus committed
308
                "the gdb process. For example, the process may not be running.");
con's avatar
con committed
309
        default:
Jarek Kobus's avatar
Jarek Kobus committed
310
            return tr("An unknown error in the gdb process occurred. ");
con's avatar
con committed
311 312 313 314 315 316 317
    }
}

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
318
    Q_UNUSED(to)
con's avatar
con committed
319 320 321 322 323 324 325 326 327 328 329
    // 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

330 331
// Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n"
// and return an exception message
332
static inline QString msgWinException(const QByteArray &data, unsigned *exCodeIn = 0)
333
{
334 335
    if (exCodeIn)
        *exCodeIn = 0;
336 337 338 339 340 341
    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);
342 343
    if (exCodeIn)
        *exCodeIn = exCode;
344 345 346 347 348 349 350 351 352
    const quint64 address = data.mid(addressPos).trimmed().toULongLong(0, 0);
    QString rc;
    QTextStream str(&rc);
    str << GdbEngine::tr("An exception was triggered: ");
    formatWindowsException(exCode, address, 0, 0, 0, str);
    str << '.';
    return rc;
}

353 354
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
355 356
    QString msg = m_outputCodec->toUnicode(data.constData(), data.length(),
        &m_outputCodecState);
357
    showMessage(msg, AppOutput);
358 359
}

hjk's avatar
hjk committed
360 361 362 363 364 365
static bool isNameChar(char c)
{
    // could be 'stopped' or 'shlibs-added'
    return (c >= 'a' && c <= 'z') || c == '-';
}

366 367 368 369 370 371 372 373
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;
374 375 376
    const bool beginFits = pos == 0 || message.at(pos - 1) == '\n';
    const bool endFits = pos + size == s || message.at(pos + size) == '\n';
    return beginFits && endFits;
377 378
}

379 380 381 382 383 384 385 386
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
387 388 389 390 391 392 393 394

    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);
395 396
}

397
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
398
{
399
    showMessage(QString::fromLocal8Bit(buff, buff.length()), LogOutput);
con's avatar
con committed
400

401 402
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
403

404 405 406
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
407

408
    int token = -1;
409
    // Token is a sequence of numbers.
410 411
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
412
            break;
413 414 415 416
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
    }
con's avatar
con committed
417

418
    // Next char decides kind of response.
419 420 421 422 423 424 425 426 427 428 429 430
    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
431

hjk's avatar
hjk committed
432
            GdbMi result;
433 434
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
435
                if (*from != ',') {
436 437
                    // happens on archer where we get
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb)
hjk's avatar
hjk committed
438
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
439 440 441 442 443
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
444 445 446
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
447 448
                }
            }
449
            if (asyncClass == "stopped") {
450
                handleStopResponse(result);
451 452
                m_pendingLogStreamOutput.clear();
                m_pendingConsoleStreamOutput.clear();
453
            } else if (asyncClass == "running") {
454
                GdbMi threads = result["thread-id"];
hjk's avatar
hjk committed
455
                threadsHandler()->notifyRunning(threads.data());
456 457 458 459
                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()))));
460 461 462
                } else {
                    notifyInferiorRunOk();
                }
463 464 465 466
            } 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",
467
                // symbols-loaded="0"
hjk's avatar
hjk committed
468 469 470 471 472

                // 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"
473
                QByteArray id = result["id"].data();
474
                if (!id.isEmpty())
475
                    showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
476
                progressPing();
477
                invalidateSourcesList();
hjk's avatar
hjk committed
478 479 480
                Module module;
                module.startAddress = 0;
                module.endAddress = 0;
481 482
                module.hostPath = _(result["host-name"].data());
                module.modulePath = _(result["target-name"].data());
hjk's avatar
hjk committed
483
                module.moduleName = QFileInfo(module.hostPath).baseName();
484
                modulesHandler()->updateModule(module);
hjk's avatar
hjk committed
485 486 487 488
            } 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"
489
                QByteArray id = result["id"].data();
490
                progressPing();
491
                showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
492
                invalidateSourcesList();
493 494
            } else if (asyncClass == "thread-group-added") {
                // 7.1-symbianelf has "{id="i1"}"
495 496 497
            } 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
498 499
                // *-started seems to be standard in 7.1, but in early
                // 7.0.x, there was a *-created instead.
500
                progressPing();
ck's avatar
ck committed
501
                // 7.1.50 has thread-group-started,id="i1",pid="3529"
502
                QByteArray id = result["id"].data();
503
                showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
ck's avatar
ck committed
504 505
                int pid = id.toInt();
                if (!pid) {
506
                    id = result["pid"].data();
ck's avatar
ck committed
507 508 509 510
                    pid = id.toInt();
                }
                if (pid)
                    notifyInferiorPid(pid);
511
                handleThreadGroupCreated(result);
512
            } else if (asyncClass == "thread-created") {
513
                //"{id="1",group-id="28902"}"
514
                QByteArray id = result["id"].data();
515
                showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
hjk's avatar
hjk committed
516 517
                ThreadData thread;
                thread.id = ThreadId(id.toLong());
518
                thread.groupId = result["group-id"].data();
hjk's avatar
hjk committed
519
                threadsHandler()->updateThread(thread);
520
            } else if (asyncClass == "thread-group-exited") {
521
                // Archer has "{id="28902"}"
522
                QByteArray id = result["id"].data();
523
                showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
524
                handleThreadGroupExited(result);
525
            } else if (asyncClass == "thread-exited") {
526
                //"{id="1",group-id="28902"}"
527 528
                QByteArray id = result["id"].data();
                QByteArray groupid = result["group-id"].data();
529
                showStatusMessage(tr("Thread %1 in group %2 exited")
hjk's avatar
hjk committed
530
                    .arg(_(id)).arg(_(groupid)), 1000);
hjk's avatar
hjk committed
531
                threadsHandler()->removeThread(ThreadId(id.toLong()));
hjk's avatar
hjk committed
532
            } else if (asyncClass == "thread-selected") {
533
                QByteArray id = result["id"].data();
534
                showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
535
                //"{id="2"}"
536 537
            } else if (m_isMacGdb && asyncClass == "shlibs-updated") {
                // Apple's gdb announces updated libs.
538
                invalidateSourcesList();
539 540
            } else if (m_isMacGdb && asyncClass == "shlibs-added") {
                // Apple's gdb announces added libs.
541 542 543 544 545
                // {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=""}}
546
                invalidateSourcesList();
547 548 549 550 551
            } 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",
552
                // line="1584",shlib="/../libFoo_debug.dylib",times="0"}
553 554
                const GdbMi bkpt = result["bkpt"];
                const BreakpointResponseId rid(bkpt["number"].data());
555
                if (!isQmlStepBreakpoint(rid)) {
hjk's avatar
hjk committed
556
                    BreakHandler *handler = breakHandler();
557
                    BreakpointModelId id = handler->findBreakpointByResponseId(rid);
hjk's avatar
hjk committed
558 559 560
                    BreakpointResponse br = handler->response(id);
                    updateResponse(br, bkpt);
                    handler->setResponse(id, br);
561
                    attemptAdjustBreakpointLocation(id);
562
                }
563 564
            } else if (asyncClass == "breakpoint-modified") {
                // New in FSF gdb since 2011-04-27.
hjk's avatar
hjk committed
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
                // "{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
580
                ba.remove(pos1, pos3 - pos1 + 1);
hjk's avatar
hjk committed
581 582 583
                result = GdbMi();
                result.fromString(ba);
                BreakHandler *handler = breakHandler();
584
                BreakpointModelId id;
hjk's avatar
hjk committed
585 586
                BreakpointResponse br;
                foreach (const GdbMi &bkpt, result.children()) {
587
                    const QByteArray nr = bkpt["number"].data();
588
                    BreakpointResponseId rid(nr);
589
                    if (!isHiddenBreakpoint(rid)) {
590 591 592 593 594 595
                        if (nr.contains('.')) {
                            // A sub-breakpoint.
                            BreakpointResponse sub;
                            updateResponse(sub, bkpt);
                            sub.id = rid;
                            sub.type = br.type;
596
                            handler->insertSubBreakpoint(id, sub);
597 598
                        } else {
                            // A primary breakpoint.
599 600 601 602 603
                            id = handler->findBreakpointByResponseId(rid);
                            //qDebug() << "NR: " << nr << "RID: " << rid
                            //    << "ID: " << id;
                            //BreakpointModelId id =
                            //    handler->findBreakpointByResponseId(rid);
604 605
                            br = handler->response(id);
                            updateResponse(br, bkpt);
606
                            handler->setResponse(id, br);
607
                        }
hjk's avatar
hjk committed
608 609
                    }
                }
610
                m_hasBreakpointNotifications = true;
611 612 613
            } else if (asyncClass == "breakpoint-created") {
                // "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
                //  addr="<PENDING>",pending="main",times="0",
614 615 616
                //  original-location="main"}}" -- or --
                // {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
                //  what="*0xbfffed48",times="0",original-location="*0xbfffed48"
617
                BreakHandler *handler = breakHandler();
618 619
                foreach (const GdbMi &bkpt, result.children()) {
                    BreakpointResponse br;
620
                    br.type = BreakpointByFileAndLine;
621
                    updateResponse(br, bkpt);
622
                    handler->handleAlienBreakpoint(br, this);
623 624 625 626 627
                }
            } else if (asyncClass == "breakpoint-deleted") {
                // "breakpoint-deleted" "{id="1"}"
                // New in FSF gdb since 2011-04-27.
                BreakHandler *handler = breakHandler();
628
                QByteArray nr = result["id"].data();
629 630
                BreakpointResponseId rid(nr);
                BreakpointModelId id = handler->findBreakpointByResponseId(rid);
631 632 633 634 635 636 637
                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);
                }
638 639 640
            } else if (asyncClass == "cmd-param-changed") {
                // New since 2012-08-09
                //  "{param="debug remote",value="1"}"
641 642 643
            } else if (asyncClass == "memory-changed") {
                // New since 2013
                //   "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
644
            } else {
645
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
646
                    << asyncClass << result.toString();
647
            }
648 649
            break;
        }
650

651
        case '~': {
652 653
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
654 655

            // Parse pid from noise.
656
            if (!inferiorPid()) {
657 658 659 660
                // 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
661 662 663
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
664 665 666 667 668
                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));
669 670
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
671
            }
672 673

            // Show some messages to give the impression something happens.
674
            if (data.startsWith("Reading symbols from ")) {
675
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
676
                progressPing();
677
                invalidateSourcesList();
678 679 680
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
681
                progressPing();
682
                showStatusMessage(_(data), 1000);
683 684 685 686
            } 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
687 688
                unsigned exCode;
                m_lastWinException = msgWinException(data, &exCode);
689
                showMessage(m_lastWinException, LogMisc);
690
                const Task::TaskType type = isFatalWinException(exCode) ? Task::Error : Task::Warning;
691
                TaskHub::addTask(type, m_lastWinException, Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
692
            }
693 694 695 696

            if (data.startsWith("QMLBP:")) {
                int pos1 = 6;
                int pos2 = data.indexOf(' ', pos1);
697
                m_qmlBreakpointResponseId2 = BreakpointResponseId(data.mid(pos1, pos2 - pos1));
698 699 700 701
                //qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
                //qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
            }

702 703
            break;
        }
704

705
        case '@': {
706
            readDebugeeOutput(GdbMi::parseCString(from, to));
707
            break;
con's avatar
con committed
708 709
        }

710 711 712 713 714 715
        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:"))
716
                showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
717

718
            if (isGdbConnectionError(data)) {
719
                notifyInferiorExited();
720
                break;
721
            }
722

723 724 725 726 727 728 729 730 731 732 733 734 735
            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
736
                    TaskHub::addTask(task);
737 738 739 740 741

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

con's avatar
con committed
744 745 746
            break;
        }

747
        case '^': {
hjk's avatar
hjk committed
748
            GdbResponse response;
con's avatar
con committed
749

hjk's avatar
hjk committed
750
            response.token = token;
con's avatar
con committed
751

752 753 754
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
755

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
756
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
757
            if (resultClass == "done")
hjk's avatar
hjk committed
758
                response.resultClass = GdbResultDone;
759
            else if (resultClass == "running")
hjk's avatar
hjk committed
760
                response.resultClass = GdbResultRunning;
761
            else if (resultClass == "connected")
hjk's avatar
hjk committed
762
                response.resultClass = GdbResultConnected;
763
            else if (resultClass == "error")
hjk's avatar
hjk committed
764
                response.resultClass = GdbResultError;
765
            else if (resultClass == "exit")
hjk's avatar
hjk committed
766
                response.resultClass = GdbResultExit;
767
            else
hjk's avatar
hjk committed
768
                response.resultClass = GdbResultUnknown;
con's avatar
con committed
769

770 771
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
772 773
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
774 775 776
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
777
                } else {
778
                    // Archer has this.
hjk's avatar
hjk committed
779 780
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
781 782
                }
            }
783 784 785

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
786 787
            response.logStreamOutput = m_pendingLogStreamOutput;
            response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
788 789 790
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

791
            handleResultRecord(&response);
792 793 794
            break;
        }
        default: {
795
            qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
796
            break;
con's avatar
con committed
797 798 799 800 801 802
        }
    }
}

void GdbEngine::readGdbStandardError()
{
ck's avatar
ck committed
803
    QByteArray err = gdbProc()->readAllStandardError();
804
    showMessage(_("UNEXPECTED GDB STDERR: " + err));
805 806
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
807 808
    if (err.startsWith("BFD: reopening"))
        return;
Jarek Kobus's avatar
Jarek Kobus committed
809
    qWarning() << "Unexpected GDB stderr:" << err;
con's avatar
con committed
810 811 812 813
}

void GdbEngine::readGdbStandardOutput()
{
814
    m_commandTimer.start(); // Restart timer.
815

816 817
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
818

hjk's avatar
hjk committed
819 820
    QByteArray out = gdbProc()->readAllStandardOutput();
    m_inbuffer.append(out);
con's avatar
con committed
821

822
    // This can trigger when a dialog starts a nested event loop.
823 824 825
    if (m_busy)
        return;

826 827
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
828
        int end = m_inbuffer.indexOf('\n', scan);
829 830 831 832 833
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
834
        scan = newstart;
835 836 837 838 839 840 841
        if (end == start)
            continue;
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
842
        m_busy = true;
hjk's avatar
hjk committed
843 844
        QByteArray ba = QByteArray::fromRawData(m_inbuffer.constData() + start, end - start);
        handleResponse(ba);
845
        m_busy = false;
con's avatar
con committed
846
    }
847
    m_inbuffer.clear();
con's avatar
con committed
848 849 850 851
}

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

855
    if (usesExecInterrupt()) {
hjk's avatar
hjk committed
856
        postCommand("-exec-interrupt", Immediate);
857 858 859
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
860
        interruptInferior2();
861
    }
con's avatar
con committed
862 863
}