gdbengine.cpp 156 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
con's avatar
con committed
29

30
31
#define QT_NO_CAST_FROM_ASCII

con's avatar
con committed
32
#include "gdbengine.h"
33
#include "gdboptionspage.h"
34
35
#include "trkoptions.h"
#include "trkoptionspage.h"
36

37
38
#include "attachgdbadapter.h"
#include "coregdbadapter.h"
hjk's avatar
hjk committed
39
#include "plaingdbadapter.h"
40
#include "termgdbadapter.h"
41
#include "remotegdbadapter.h"
42
#include "trkgdbadapter.h"
con's avatar
con committed
43

44
#include "watchutils.h"
45
#include "debuggeractions.h"
46
#include "debuggeragents.h"
con's avatar
con committed
47
48
#include "debuggerconstants.h"
#include "debuggermanager.h"
49
#include "debuggertooltip.h"
50
#include "debuggerstringutils.h"
con's avatar
con committed
51
52
53
54
55
56
57
#include "gdbmi.h"

#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
58
#include "sourcefileswindow.h"
con's avatar
con committed
59

60
#include "debuggerdialogs.h"
con's avatar
con committed
61

hjk's avatar
hjk committed
62
#include <utils/qtcassert.h>
63
#include <utils/fancymainwindow.h>
64
#include <texteditor/itexteditor.h>
65
#include <projectexplorer/toolchain.h>
66
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
67

con's avatar
con committed
68
69
70
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
71
#include <QtCore/QMetaObject>
con's avatar
con committed
72
73
#include <QtCore/QTime>
#include <QtCore/QTimer>
74
#include <QtCore/QTextStream>
con's avatar
con committed
75
76

#include <QtGui/QAction>
77
#include <QtCore/QCoreApplication>
con's avatar
con committed
78
79
80
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
81
82
#include <QtGui/QDialogButtonBox>
#include <QtGui/QPushButton>
83
#ifdef Q_OS_WIN
84
#    include "shared/sharedlibraryinjector.h"
85
#endif
con's avatar
con committed
86

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
87
#ifdef Q_OS_UNIX
con's avatar
con committed
88
89
90
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
91
#include <ctype.h>
con's avatar
con committed
92

93
94
namespace Debugger {
namespace Internal {
con's avatar
con committed
95
96

//#define DEBUG_PENDING  1
97
//#define DEBUG_SUBITEM  1
con's avatar
con committed
98
99
100
101

#if DEBUG_PENDING
#   define PENDING_DEBUG(s) qDebug() << s
#else
Roberto Raggi's avatar
Roberto Raggi committed
102
#   define PENDING_DEBUG(s)
con's avatar
con committed
103
#endif
104
#define PENDING_DEBUGX(s) qDebug() << s
con's avatar
con committed
105

106
107
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

hjk's avatar
hjk committed
108
static bool stateAcceptsGdbCommands(DebuggerState state)
hjk's avatar
hjk committed
109
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
    switch (state) {
    case AdapterStarting:
    case AdapterStarted:
    case AdapterStartFailed:
    case InferiorUnrunnable:
    case InferiorStarting:
    case InferiorStartFailed:
    case InferiorRunningRequested:
    case InferiorRunning:
    case InferiorStopping:
    case InferiorStopped:
    case InferiorShuttingDown:
    case InferiorShutDown:
    case InferiorShutdownFailed:
        return true;
    case DebuggerNotReady:
    case EngineStarting:
    case InferiorStopFailed:
    case EngineShuttingDown:
        break;
    }
    return false;
}
hjk's avatar
hjk committed
133

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

140
// reads a MI-encoded item frome the consolestream
hjk's avatar
hjk committed
141
static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
142
{
hjk's avatar
hjk committed
143
    GdbMi output = response.data.findChild("consolestreamoutput");
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
    QByteArray out = output.data();

    int markerPos = out.indexOf('"') + 1; // position of 'success marker'
    if (markerPos == 0 || out.at(markerPos) == 'f') {  // 't' or 'f'
        // custom dumper produced no output
        return false;
    }

    out = out.mid(markerPos +  1);
    out = out.left(out.lastIndexOf('"'));
    // optimization: dumper output never needs real C unquoting
    out.replace('\\', "");
    out = "dummy={" + out + "}";

    contents->fromString(out);
    //qDebug() << "CONTENTS" << contents->toString(true);
    return contents->isValid();
}

hjk's avatar
hjk committed
163
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
164
{
hjk's avatar
hjk committed
165
    GdbMi output = response.data.findChild("consolestreamoutput");
166
167
168
169
170
171
172
173
174
175
    QByteArray out = output.data();
    // 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);
}

con's avatar
con committed
176
177
178
179
180
181
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

hjk's avatar
hjk committed
182
183
GdbEngine::GdbEngine(DebuggerManager *manager) :
    IDebuggerEngine(manager),
184
#ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
hjk's avatar
hjk committed
185
    m_dumperInjectionLoad(true)
186
#else
hjk's avatar
hjk committed
187
    m_dumperInjectionLoad(false)
188
#endif
con's avatar
con committed
189
{
190
191
    m_trkOptions = QSharedPointer<TrkOptions>(new TrkOptions);
    m_trkOptions->fromSettings(Core::ICore::instance()->settings());
hjk's avatar
hjk committed
192
    m_gdbAdapter = 0;
193

194
195
196
    // Needs no resetting in initializeVariables()
    m_busy = false;

197
198
    connect(theDebuggerAction(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setAutoDerefPointers(QVariant)));
199
}
200

201
void GdbEngine::connectDebuggingHelperActions()
202
{
203
204
205
206
207
208
    connect(theDebuggerAction(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setUseDebuggingHelpers(QVariant)));
    connect(theDebuggerAction(DebugDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setDebugDebuggingHelpers(QVariant)));
    connect(theDebuggerAction(RecheckDebuggingHelpers), SIGNAL(triggered()),
            this, SLOT(recheckDebuggingHelperAvailability()));
209
210
}
   
211
212
213
214
215
216
217
void GdbEngine::disconnectDebuggingHelperActions()
{
    disconnect(theDebuggerAction(UseDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(DebugDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(RecheckDebuggingHelpers), 0, this, 0);
}

218
219
220
221
DebuggerStartMode GdbEngine::startMode() const
{
    QTC_ASSERT(!m_startParameters.isNull(), return NoStartMode);
    return m_startParameters->startMode;
con's avatar
con committed
222
223
}

224
225
226
227
228
QMainWindow *GdbEngine::mainWindow() const
{
    return m_manager->mainWindow();
}

con's avatar
con committed
229
230
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
231
    // prevent sending error messages afterwards
232
    disconnect(&m_gdbProc, 0, this, 0);
233
    delete m_gdbAdapter;
234
    m_gdbAdapter = 0;
hjk's avatar
hjk committed
235
236
}

237
void GdbEngine::connectAdapter()
con's avatar
con committed
238
{
hjk's avatar
hjk committed
239
240
    connect(m_gdbAdapter, SIGNAL(adapterStarted()),
        this, SLOT(handleAdapterStarted()));
241
242
    connect(m_gdbAdapter, SIGNAL(adapterStartFailed(QString,QString)),
        this, SLOT(handleAdapterStartFailed(QString,QString)));
hjk's avatar
hjk committed
243

244
245
246
    connect(m_gdbAdapter, SIGNAL(inferiorPrepared()),
        this, SLOT(handleInferiorPrepared()));

hjk's avatar
hjk committed
247
248
249
    connect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
        this, SLOT(handleInferiorStartFailed(QString)));

250
251
    connect(m_gdbAdapter, SIGNAL(adapterCrashed(QString)),
        this, SLOT(handleAdapterCrashed(QString)));
252
}
253

254
255
void GdbEngine::initializeVariables()
{
256
    m_debuggingHelperState = DebuggingHelperUninitialized;
257
    m_gdbVersion = 100;
hjk's avatar
hjk committed
258
    m_gdbBuildVersion = -1;
259
    m_isMacGdb = false;
hjk's avatar
hjk committed
260
    m_isSynchroneous = false;
261
262
263
264
265

    m_fullToShortName.clear();
    m_shortToFullName.clear();
    m_varToType.clear();

266
267
    m_modulesListOutdated = m_sourcesListOutdated = true;
    m_sourcesListUpdating = false;
268
269
270
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
271
    m_commandsDoneCallback = 0;
272
    m_commandsToRunOnTemporaryBreak.clear();
273
    m_cookieForToken.clear();
274
275
276
277
278
279

    m_pendingConsoleStreamOutput.clear();
    m_pendingLogStreamOutput.clear();

    m_inbuffer.clear();

280
281
282
283
    // ConverterState has no reset() function.
    m_outputCodecState.~ConverterState();
    new (&m_outputCodecState) QTextCodec::ConverterState();

284
285
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
286
    m_dumperHelper.clear();
287
288
}

289
QString GdbEngine::errorMessage(QProcess::ProcessError error)
con's avatar
con committed
290
291
292
{
    switch (error) {
        case QProcess::FailedToStart:
293
            return tr("The Gdb process failed to start. Either the "
con's avatar
con committed
294
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
295
                "permissions to invoke the program.")
hjk's avatar
hjk committed
296
                .arg(theDebuggerStringSetting(GdbLocation));
con's avatar
con committed
297
        case QProcess::Crashed:
298
            return tr("The Gdb process crashed some time after starting "
con's avatar
con committed
299
300
                "successfully.");
        case QProcess::Timedout:
301
            return tr("The last waitFor...() function timed out. "
con's avatar
con committed
302
303
304
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
        case QProcess::WriteError:
305
            return tr("An error occurred when attempting to write "
con's avatar
con committed
306
307
308
                "to the Gdb process. For example, the process may not be running, "
                "or it may have closed its input channel.");
        case QProcess::ReadError:
309
            return tr("An error occurred when attempting to read from "
con's avatar
con committed
310
311
                "the Gdb process. For example, the process may not be running.");
        default:
312
            return tr("An unknown error in the Gdb process occurred. ");
con's avatar
con committed
313
314
315
316
317
318
319
    }
}

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

332
333
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
334
    m_manager->showApplicationOutput(m_outputCodec->toUnicode(
335
336
337
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
338
339
void GdbEngine::debugMessage(const QString &msg)
{
340
    gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
341
342
}

343
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
344
345
346
{
    static QTime lastTime;

347
    if (theDebuggerBoolSetting(LogTimeStamps))
348
349
        gdbOutputAvailable(LogTime, currentTime());
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
350
351
352
353
354

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
355
356
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
357
        << "size:" << buff.size();
con's avatar
con committed
358
#else
359
    //qDebug() << "buf:" << buff;
con's avatar
con committed
360
361
362
363
#endif

    lastTime = QTime::currentTime();

364
365
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
366

367
368
369
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
370

371
372
373
374
    int token = -1;
    // token is a sequence of numbers
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
375
            break;
376
377
378
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
379
        //qDebug() << "found token" << token;
380
    }
con's avatar
con committed
381

hjk's avatar
hjk committed
382
    // next char decides kind of response
383
384
385
386
387
388
389
390
391
392
393
394
395
396
    const char c = *from++;
    //qDebug() << "CODE:" << c;
    switch (c) {
        case '*':
        case '+':
        case '=': {
            QByteArray asyncClass;
            for (; from != to; ++from) {
                const char c = *from;
                if (!isNameChar(c))
                    break;
                asyncClass += *from;
            }
            //qDebug() << "ASYNCCLASS" << asyncClass;
con's avatar
con committed
397

hjk's avatar
hjk committed
398
            GdbMi result;
399
400
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
401
                if (*from != ',') {
hjk's avatar
hjk committed
402
403
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
hjk's avatar
hjk committed
404
                    result.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
405
406
407
408
409
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
hjk's avatar
hjk committed
410
411
412
                    //qDebug() << "parsed result:" << data.toString();
                    result.m_children += data;
                    result.m_type = GdbMi::Tuple;
con's avatar
con committed
413
414
                }
            }
415
            if (asyncClass == "stopped") {
416
                handleStopResponse(result);
417
418
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
419
420
421
422
            } 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",
423
                // symbols-loaded="0"
hjk's avatar
hjk committed
424
                QByteArray id = result.findChild("id").data();
425
                if (!id.isEmpty())
hjk's avatar
hjk committed
426
                    showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
427
                m_modulesListOutdated = m_sourcesListOutdated = true;
hjk's avatar
hjk committed
428
429
430
431
            } 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"
hjk's avatar
hjk committed
432
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
433
                showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
434
                m_modulesListOutdated = m_sourcesListOutdated = true;
435
436
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
hjk's avatar
hjk committed
437
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
438
                showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
439
440
441
                int pid = id.toInt();
                if (pid != inferiorPid())
                    handleInferiorPidChanged(pid);
442
443
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
hjk's avatar
hjk committed
444
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
445
                showStatusMessage(tr("Thread %1 created.").arg(_(id)));
446
447
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
hjk's avatar
hjk committed
448
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
449
                showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
450
451
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
hjk's avatar
hjk committed
452
453
                QByteArray id = result.findChild("id").data();
                QByteArray groupid = result.findChild("group-id").data();
hjk's avatar
hjk committed
454
                showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
455
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
456
            } else if (asyncClass == "thread-selected") {
hjk's avatar
hjk committed
457
                QByteArray id = result.findChild("id").data();
hjk's avatar
hjk committed
458
                showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
459
                //"{id="2"}" 
460
            #if defined(Q_OS_MAC)
461
462
            } else if (asyncClass == "shlibs-updated") {
                // MAC announces updated libs
463
                m_modulesListOutdated = m_sourcesListOutdated = true;
464
465
466
467
468
469
470
            } else if (asyncClass == "shlibs-added") {
                // MAC announces added libs
                // {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=""}}
471
                m_modulesListOutdated = m_sourcesListOutdated = true;
472
473
            #endif
            } else {
474
                qDebug() << "IGNORED ASYNC OUTPUT"
hjk's avatar
hjk committed
475
                    << asyncClass << result.toString();
476
            }
477
478
            break;
        }
479

480
        case '~': {
481
482
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
483
484
485
486
487
488
489

            // Parse pid from noise.
            if (!inferiorPid()) {
                // 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
490
491
492
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
493
494
495
496
497
                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));
498
499
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
500
            }
501
502

            // Show some messages to give the impression something happens.
503
            if (data.startsWith("Reading symbols from ")) {
504
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
505
                m_modulesListOutdated = m_sourcesListOutdated = true;
506
507
508
            } else if (data.startsWith("[New ") || data.startsWith("[Thread ")) {
                if (data.endsWith('\n'))
                    data.chop(1);
509
                showStatusMessage(_(data), 1000);
510
            }
511
512
            break;
        }
513

514
        case '@': {
515
            readDebugeeOutput(GdbMi::parseCString(from, to));
516
            break;
con's avatar
con committed
517
518
        }

519
520
521
522
523
524
        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:"))
hjk's avatar
hjk committed
525
                manager()->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
526
527
528
            break;
        }

529
        case '^': {
hjk's avatar
hjk committed
530
            GdbResponse response;
con's avatar
con committed
531

hjk's avatar
hjk committed
532
            response.token = token;
con's avatar
con committed
533

534
535
536
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
537

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
538
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
539
            if (resultClass == "done") {
hjk's avatar
hjk committed
540
                response.resultClass = GdbResultDone;
541
542
543
            } else if (resultClass == "running") {
                setState(InferiorRunning);
                showStatusMessage(tr("Running..."));
hjk's avatar
hjk committed
544
                response.resultClass = GdbResultRunning;
545
            } else if (resultClass == "connected") {
hjk's avatar
hjk committed
546
                response.resultClass = GdbResultConnected;
547
            } else if (resultClass == "error") {
hjk's avatar
hjk committed
548
                response.resultClass = GdbResultError;
549
            } else if (resultClass == "exit") {
hjk's avatar
hjk committed
550
                response.resultClass = GdbResultExit;
551
            } else {
hjk's avatar
hjk committed
552
                response.resultClass = GdbResultUnknown;
553
            }
con's avatar
con committed
554

555
556
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
557
558
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
559
560
561
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
562
563
                } else {
                    // Archer has this
hjk's avatar
hjk committed
564
565
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
566
567
                }
            }
568
569
570

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
hjk's avatar
hjk committed
571
            response.data.setStreamOutput("logstreamoutput",
572
                m_pendingLogStreamOutput);
hjk's avatar
hjk committed
573
            response.data.setStreamOutput("consolestreamoutput",
574
575
576
577
                m_pendingConsoleStreamOutput);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

578
            handleResultRecord(&response);
579
580
581
582
583
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
584
585
586
587
588
589
        }
    }
}

void GdbEngine::readGdbStandardError()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
590
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
591
592
593
594
}

void GdbEngine::readGdbStandardOutput()
{
595
596
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
597

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
598
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
599

600
601
602
603
    // This can trigger when a dialog starts a nested event loop
    if (m_busy)
        return;

604
605
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
606
        int end = m_inbuffer.indexOf('\n', scan);
607
608
609
610
611
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
612
        scan = newstart;
613
614
        if (end == start)
            continue;
615
        #if defined(Q_OS_WIN)
616
617
618
619
620
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
621
        #endif
622
        m_busy = true;
623
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
624
        m_busy = false;
con's avatar
con committed
625
    }
626
    m_inbuffer.clear();
con's avatar
con committed
627
628
629
630
}

void GdbEngine::interruptInferior()
{
631
    QTC_ASSERT(state() == InferiorRunning, qDebug() << state(); return);
con's avatar
con committed
632

hjk's avatar
hjk committed
633
634
635
    setState(InferiorStopping);
    showStatusMessage(tr("Stop requested..."), 5000);

636
    debugMessage(_("TRYING TO INTERRUPT INFERIOR"));
hjk's avatar
hjk committed
637
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
638
639
640
641
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
642
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
643
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
644
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
645
646
        return;
    }
hjk's avatar
hjk committed
647
    if (pid == inferiorPid())
con's avatar
con committed
648
        return;
649
650
    debugMessage(_("FOUND PID %1").arg(pid));    

651
    handleInferiorPidChanged(pid);
652
653
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
654
655
}

hjk's avatar
hjk committed
656
657
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
658
659
660
661
662
663
664
{
    postCommand(command, NoFlags, callback, callbackName, cookie);
}

void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
                            AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
hjk's avatar
hjk committed
665
666
667
{
    GdbCommand cmd;
    cmd.command = command;
668
    cmd.flags = flags;
hjk's avatar
hjk committed
669
670
671
672
673
674
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

675
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
676
677
                            const char *callbackName, const QVariant &cookie)
{
678
    postCommand(command, NoFlags, callback, callbackName, cookie);
679
680
}

681
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
682
683
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
684
685
686
687
688
689
690
691
692
693
694
{
    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
695
{
hjk's avatar
hjk committed
696
    if (!stateAcceptsGdbCommands(state())) {
hjk's avatar
hjk committed
697
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
698
699
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
            .arg(cmd.command).arg(state()));
con's avatar
con committed
700
701
702
        return;
    }

hjk's avatar
hjk committed
703
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
704
        ++m_pendingRequests;
705
706
        PENDING_DEBUG("   MODEL:" << cmd.command << "=>" << cmd.callbackName
                      << "INCREMENTS PENDING TO" << m_pendingRequests);
con's avatar
con committed
707
    } else {
708
709
        PENDING_DEBUG("   OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
710
711
    }

712
    if ((cmd.flags & NeedsStop) || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
713
714
        if (state() == InferiorStopped
            || state() == InferiorStarting || state() == AdapterStarted) {
hjk's avatar
hjk committed
715
716
717
718
719
720
            // Can be safely sent now.
            flushCommand(cmd);
        } else {
            // Queue the commands that we cannot send at once.
            debugMessage(_("QUEUING COMMAND ") + cmd.command);
            m_commandsToRunOnTemporaryBreak.append(cmd);
721
722
723
724
            if (state() != InferiorStopping) {
                showStatusMessage(tr("Stopping temporarily."), 1000);
                interruptInferior(); // FIXME: race condition between gdb and kill()
            }
hjk's avatar
hjk committed
725
        }
hjk's avatar
hjk committed
726
    } else if (!cmd.command.isEmpty()) {
727
        flushCommand(cmd);
con's avatar
con committed
728
729
730
    }
}

hjk's avatar
hjk committed
731
void GdbEngine::flushCommand(const GdbCommand &cmd0)
732
{
hjk's avatar
hjk committed
733
    GdbCommand cmd = cmd0;
hjk's avatar
hjk committed
734
    if (state() == DebuggerNotReady) {
735
        gdbInputAvailable(LogInput, cmd.command);
736
737
738
739
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

740
    ++currentToken();
741
    cmd.postTime = QTime::currentTime();
742
743
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
744
    if (cmd.flags & EmbedToken)
745
        cmd.command = cmd.command.arg(currentToken());
746
    gdbInputAvailable(LogInput, cmd.command);
747
748

    m_gdbAdapter->write(cmd.command.toLatin1() + "\r\n");
749
750
}

751
void GdbEngine::handleResultRecord(GdbResponse *response)
con's avatar
con committed
752
{
hjk's avatar
hjk committed
753
    //qDebug() << "TOKEN:" << response.token
754
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
hjk's avatar
hjk committed
755
    //qDebug() << "\nRESULT" << response.token << response.toString();
con's avatar
con committed
756

757
    int token = response->token;
con's avatar
con committed
758
759
760
    if (token == -1)
        return;

761
    if (!m_cookieForToken.contains(token)) {
762
763
764
765
        // In theory this should not happen (rather the error should be
        // reported in the "first" response to the command) in practice it
        // does. We try to handle a few situations we are aware of gracefully.
        // Ideally, this code should not be present at all.
766
767
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
768
769
        if (response->resultClass == GdbResultError) {
            QByteArray msg = response->data.findChild("msg").data();
770
771
772
773
774
775
776
            if (msg == "Cannot find new threads: generic error") {
                // Handle a case known to occur on Linux/gdb 6.8 when debugging moc
                // with helpers enabled. In this case we get a second response with
                // msg="Cannot find new threads: generic error"
                showMessageBox(QMessageBox::Critical,
                    tr("Executable failed"), QString::fromLocal8Bit(msg));
                showStatusMessage(tr("Process failed to start."));
777
                shutdown();
778
779
780
            } else if (msg == "\"finish\" not meaningful in the outermost frame.") { 
                // Handle a case known to appear on gdb 6.4 symbianelf when
                // the stack is cut due to access to protected memory.
781
782
                setState(InferiorStopping);
                setState(InferiorStopped);
783
784
785
786
787
788
789
790
791
792
793
            } else if (msg.startsWith("Cannot find bounds of current function")) {
                // Happens when running "-exec-next" in a function for which
                // there is no debug information. Divert to "-exec-next-step"
                setState(InferiorStopping);
                setState(InferiorStopped);
                nextIExec();
            } else {
                showMessageBox(QMessageBox::Critical,
                    tr("Executable failed"), QString::fromLocal8Bit(msg));
                showStatusMessage(tr("Executable failed: %1")
                    .arg(QString::fromLocal8Bit(msg)));
794
            }
795
796
797
798
        }
        return;
    }

799
    GdbCommand cmd = m_cookieForToken.take(token);
800
    if (theDebuggerBoolSetting(LogTimeStamps)) {
801
        gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
802
803
804
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
805

806
    if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
807
        //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
con's avatar
con committed
808
809
810
        return;
    }

811
    response->cookie = cmd.cookie;
hjk's avatar
hjk committed
812

813
814
815
816
817
    if (response->resultClass != GdbResultError &&
        response->resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
                                  (cmd.flags & ExitRequest) ? GdbResultExit :
                                  GdbResultDone)) {
        QString rsp = _(GdbResponse::stringFromResultClass(response->resultClass));
818
819
        qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
        debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
820
821
    } else {
        if (cmd.callback)
822
            (this->*cmd.callback)(*response);
823
        else if (cmd.adapterCallback)
824
            (m_gdbAdapter->*cmd.adapterCallback)(*response);
825
    }
con's avatar
con committed
826

827
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
828
        --m_pendingRequests;
829
830
        PENDING_DEBUG("   WATCH" << cmd.command << "=>" << cmd.callbackName
                      << "DECREMENTS PENDING TO" << m_pendingRequests);
831
        if (m_pendingRequests <= 0) {
832
            PENDING_DEBUG("\n\n ... AND TRIGGERS MODEL UPDATE\n");
hjk's avatar
hjk committed
833
            rebuildModel();
834
        }
con's avatar
con committed
835
    } else {
836
837
        PENDING_DEBUG("   OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
838
    }
839

840
841
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
842
843
844
845
    // This is somewhat inefficient, as it makes the last command synchronous.
    // An optimization would be requesting the continue immediately when the
    // event loop is entered, and let individual commands have a flag to suppress
    // that behavior.
846
    if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
847
        debugMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
848
849
        CommandsDoneCallback cont = m_commandsDoneCallback;
        m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
850
851
852
        (this->*cont)();
    } else {
        PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
853
    }
con's avatar
con committed
854
855
856
857
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
hjk's avatar
hjk committed
858
    if (state() == DebuggerNotReady) {
859
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
860
861
862
        return;
    }

hjk's avatar
hjk committed
863
    m_gdbAdapter->write(command.toLatin1() + "\r\n");
con's avatar
con committed
864
865
}

866
// Called from CoreAdapter and AttachAdapter
867
void GdbEngine::updateAll()
868
{
869
    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
870
    tryLoadDebuggingHelpers();
hjk's avatar
hjk committed
871
872
    postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames),
        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
873
    manager()->stackHandler()->setCurrentIndex(0);
874
    if (supportsThreads())
875
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
hjk's avatar
hjk committed
876
    manager()->reloadRegisters();
hjk's avatar
hjk committed
877
    updateLocals(); 
878
879
}

hjk's avatar
hjk committed
880
void GdbEngine::handleQuerySources(const GdbResponse &response)
con's avatar
con committed
881
{
hjk's avatar
hjk committed
882
    if (response.resultClass == GdbResultDone) {
883
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
884
885
886
887
        m_shortToFullName.clear();
        m_fullToShortName.clear();
        // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
        // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
hjk's avatar
hjk committed
888
        GdbMi files = response.data.findChild("files");
con's avatar
con committed
889
890
        foreach (const GdbMi &item, files.children()) {
            GdbMi fullName = item.findChild("fullname");
891
            if (fullName.isValid()) {
Oswald Buddenhagen's avatar