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();
        }
    }
}

void GdbEngine::handleInterruptDeviceInferior(const QString &error)
{
    if (error.isEmpty()) {
        showMessage(QLatin1String("Interrupted ") + QString::number(inferiorPid()));
        notifyInferiorStopOk();
    } else {
        showMessage(error, LogError);
        notifyInferiorStopFailed();
873
    }
874
875
    m_signalOperation->disconnect(this);
    m_signalOperation.clear();
con's avatar
con committed
876
877
}

878
879
void GdbEngine::interruptInferiorTemporarily()
{
880
    foreach (const GdbCommand &cmd, m_commandsToRunOnTemporaryBreak) {
881
        if (cmd.flags & LosesChild) {
hjk's avatar
hjk committed
882
            notifyInferiorIll();
883
            return;
884
        }
885
    }
886
    requestInterruptInferior();
887
888
}

con's avatar
con committed
889
890
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
891
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
892
    if (pid == 0) {
893
        showMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
894
895
        return;
    }
896
    if (pid == inferiorPid())
con's avatar
con committed
897
        return;
898

899
900
    showMessage(_("FOUND PID %1").arg(pid));
    notifyInferiorPid(pid);
con's avatar
con committed
901
902
}

903
void GdbEngine::postCommand(const QByteArray &command, GdbCommandCallback callback,
904
905
                            const char *callbackName, const QVariant &cookie)
{
906
    postCommand(command, NoFlags, callback, callbackName, cookie);
907
908
}

909
void GdbEngine::postCommand(const QByteArray &command, GdbCommandFlags flags,
910
911
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
912
913
914
915
916
917
918
919
920
921
922
{
    GdbCommand cmd;
    cmd.command = command;
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

void GdbEngine::postCommandHelper(const GdbCommand &cmd)
con's avatar
con committed
923
{
hjk's avatar
hjk committed
924
    if (!stateAcceptsGdbCommands(state())) {
925
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.command));
926
        showMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")