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 447 448
                } 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.
449 450 451
                } else {
                    notifyInferiorRunOk();
                }
452 453 454 455
            } 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",
456
                // symbols-loaded="0"
hjk's avatar
hjk committed
457 458 459 460 461

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

640
        case '~': {
641 642
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
643 644

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

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

            if (data.startsWith("QMLBP:")) {
                int pos1 = 6;
                int pos2 = data.indexOf(' ', pos1);
686
                m_qmlBreakpointResponseId2 = BreakpointResponseId(data.mid(pos1, pos2 - pos1));
687 688 689 690
                //qDebug() << "FOUND QMLBP: " << m_qmlBreakpointNumbers[2];
                //qDebug() << "CURRENT: " << m_qmlBreakpointNumbers;
            }

691 692
            break;
        }
693

694
        case '@': {
695
            readDebugeeOutput(GdbMi::parseCString(from, to));
696
            break;
con's avatar
con committed
697 698
        }

699 700 701 702 703 704
        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:"))
705
                showMessage(_(data.mid(9)), AppStuff); // Cut "warning: "
706

707
            if (isGdbConnectionError(data)) {
708
                notifyInferiorExited();
709
                break;
710
            }
711

712 713 714 715 716 717 718 719 720 721 722 723 724
            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
725
                    TaskHub::addTask(task);
726 727 728 729 730

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

con's avatar
con committed
733 734 735
            break;
        }

736
        case '^': {
hjk's avatar
hjk committed
737
            GdbResponse response;
con's avatar
con committed
738

hjk's avatar
hjk committed
739
            response.token = token;
con's avatar
con committed
740

741 742 743
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
744

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

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

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
775 776
            response.logStreamOutput = m_pendingLogStreamOutput;
            response.consoleStreamOutput =  m_pendingConsoleStreamOutput;
777 778 779
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

780
            handleResultRecord(&response);
781 782 783
            break;
        }
        default: {
784
            qDebug() << "UNKNOWN RESPONSE TYPE '" << c << "'. REST: " << from;
785
            break;
con's avatar
con committed
786 787 788 789 790 791
        }
    }
}

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

void GdbEngine::readGdbStandardOutput()
{
803
    m_commandTimer.start(); // Restart timer.
804

805 806
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
807

808
    QByteArray out = m_gdbProc->readAllStandardOutput();
hjk's avatar
hjk committed
809
    m_inbuffer.append(out);
con's avatar
con committed
810

811
    // This can trigger when a dialog starts a nested event loop.
812 813 814
    if (m_busy)
        return;

815 816
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
817
        int end = m_inbuffer.indexOf('\n', scan);
818 819 820 821 822
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
823
        scan = newstart;
824 825 826 827 828 829 830
        if (end == start)
            continue;
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
831
        m_busy = true;
hjk's avatar
hjk committed
832 833
        QByteArray ba = QByteArray::fromRawData(m_inbuffer.constData() + start, end - start);
        handleResponse(ba);
834
        m_busy = false;
con's avatar
con committed
835
    }
836
    m_inbuffer.clear();
con's avatar
con committed
837 838 839 840
}

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

844
    if (usesExecInterrupt()) {
hjk's avatar
hjk committed
845
        postCommand("-exec-interrupt", Immediate);
846 847 848
    } else {
        showStatusMessage(tr("Stop requested..."), 5000);
        showMessage(_("TRYING TO INTERRUPT INFERIOR"));
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
        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();
        }
    }