gdbengine.cpp 157 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
    m_registerNamesListed = false;
262
263
264
265
266

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

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

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

    m_inbuffer.clear();

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

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

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

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

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

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

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

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

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

    lastTime = QTime::currentTime();

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

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

372
373
374
375
    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
376
            break;
377
378
379
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
380
        //qDebug() << "found token" << token;
381
    }
con's avatar
con committed
382

hjk's avatar
hjk committed
383
    // next char decides kind of response
384
385
386
387
388
389
390
391
392
393
394
395
396
397
    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
398

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

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

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

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

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

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

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

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

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
539
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
540
            if (resultClass == "done") {
hjk's avatar
hjk committed
541
                response.resultClass = GdbResultDone;
542
            } else if (resultClass == "running") {
543
544
545
546
547
                if (state() == InferiorStopped) { // Result of manual command.
                    m_manager->resetLocation();
                    setTokenBarrier();
                    setState(InferiorRunningRequested);
                }
548
549
                setState(InferiorRunning);
                showStatusMessage(tr("Running..."));
hjk's avatar
hjk committed
550
                response.resultClass = GdbResultRunning;
551
            } else if (resultClass == "connected") {
hjk's avatar
hjk committed
552
                response.resultClass = GdbResultConnected;
553
            } else if (resultClass == "error") {
hjk's avatar
hjk committed
554
                response.resultClass = GdbResultError;
555
            } else if (resultClass == "exit") {
hjk's avatar
hjk committed
556
                response.resultClass = GdbResultExit;
557
            } else {
hjk's avatar
hjk committed
558
                response.resultClass = GdbResultUnknown;
559
            }
con's avatar
con committed
560

561
562
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
563
564
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
565
566
567
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
568
569
                } else {
                    // Archer has this
hjk's avatar
hjk committed
570
571
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
572
573
                }
            }
574
575
576

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
hjk's avatar
hjk committed
577
            response.data.setStreamOutput("logstreamoutput",
578
                m_pendingLogStreamOutput);
hjk's avatar
hjk committed
579
            response.data.setStreamOutput("consolestreamoutput",
580
581
582
583
                m_pendingConsoleStreamOutput);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

584
            handleResultRecord(&response);
585
586
587
588
589
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
590
591
592
593
594
595
        }
    }
}

void GdbEngine::readGdbStandardError()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
596
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
597
598
599
600
}

void GdbEngine::readGdbStandardOutput()
{
601
602
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
603

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
604
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
605

606
607
608
609
    // This can trigger when a dialog starts a nested event loop
    if (m_busy)
        return;

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

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

hjk's avatar
hjk committed
639
640
641
    setState(InferiorStopping);
    showStatusMessage(tr("Stop requested..."), 5000);

642
    debugMessage(_("TRYING TO INTERRUPT INFERIOR"));
hjk's avatar
hjk committed
643
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
644
645
646
647
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
648
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
649
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
650
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
651
652
        return;
    }
hjk's avatar
hjk committed
653
    if (pid == inferiorPid())
con's avatar
con committed
654
        return;
655
656
    debugMessage(_("FOUND PID %1").arg(pid));    

657
    handleInferiorPidChanged(pid);
658
659
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
660
661
}

hjk's avatar
hjk committed
662
663
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
664
665
666
667
668
669
670
{
    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
671
672
673
{
    GdbCommand cmd;
    cmd.command = command;
674
    cmd.flags = flags;
hjk's avatar
hjk committed
675
676
677
678
679
680
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

681
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
682
683
                            const char *callbackName, const QVariant &cookie)
{
684
    postCommand(command, NoFlags, callback, callbackName, cookie);
685
686
}

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

hjk's avatar
hjk committed
709
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
710
        ++m_pendingRequests;
711
712
        PENDING_DEBUG("   MODEL:" << cmd.command << "=>" << cmd.callbackName
                      << "INCREMENTS PENDING TO" << m_pendingRequests);
con's avatar
con committed
713
    } else {
714
715
        PENDING_DEBUG("   OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
716
717
    }

718
    if ((cmd.flags & NeedsStop) || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
719
720
        if (state() == InferiorStopped
            || state() == InferiorStarting || state() == AdapterStarted) {
hjk's avatar
hjk committed
721
722
723
724
725
726
            // 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);
727
728
729
730
            if (state() != InferiorStopping) {
                showStatusMessage(tr("Stopping temporarily."), 1000);
                interruptInferior(); // FIXME: race condition between gdb and kill()
            }
hjk's avatar
hjk committed
731
        }
hjk's avatar
hjk committed
732
    } else if (!cmd.command.isEmpty()) {
733
        flushCommand(cmd);
con's avatar
con committed
734
735
736
    }
}

hjk's avatar
hjk committed
737
void GdbEngine::flushCommand(const GdbCommand &cmd0)
738
{
hjk's avatar
hjk committed
739
    GdbCommand cmd = cmd0;
hjk's avatar
hjk committed
740
    if (state() == DebuggerNotReady) {
741
        gdbInputAvailable(LogInput, cmd.command);
742
743
744
745
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

746
    ++currentToken();
747
    cmd.postTime = QTime::currentTime();
748
749
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
750
    if (cmd.flags & EmbedToken)
751
        cmd.command = cmd.command.arg(currentToken());
752
    gdbInputAvailable(LogInput, cmd.command);
753
754

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

757
void GdbEngine::handleResultRecord(GdbResponse *response)
con's avatar
con committed
758
{
hjk's avatar
hjk committed
759
    //qDebug() << "TOKEN:" << response.token
760
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
hjk's avatar
hjk committed
761
    //qDebug() << "\nRESULT" << response.token << response.toString();
con's avatar
con committed
762

763
    int token = response->token;
con's avatar
con committed
764
765
766
    if (token == -1)
        return;

767
    if (!m_cookieForToken.contains(token)) {
768
769
770
771
        // 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.
772
773
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
774
775
        if (response->resultClass == GdbResultError) {
            QByteArray msg = response->data.findChild("msg").data();
776
777
778
779
            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"
780
                debugMessage(_("APPLYING WORKAROUND #1"));
781
782
783
                showMessageBox(QMessageBox::Critical,
                    tr("Executable failed"), QString::fromLocal8Bit(msg));
                showStatusMessage(tr("Process failed to start."));
784
                shutdown();
785
786
787
            } 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.
788
                debugMessage(_("APPLYING WORKAROUND #2"));
789
790
                setState(InferiorStopping);
                setState(InferiorStopped);
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"
794
                debugMessage(_("APPLYING WORKAROUND #3"));
795
796
797
                setState(InferiorStopping);
                setState(InferiorStopped);
                nextIExec();
798
799
800
801
802
803
804
805
806
807
808
809
            } else if (msg.startsWith("Couldn't get registers: No such process.")) {
                // Happens on archer-tromey-python 6.8.50.20090910-cvs
                // There might to be a race between a process shutting down
                // and library load messages.
                debugMessage(_("APPLYING WORKAROUND #4"));
                setState(InferiorStopping);
                setState(InferiorStopped);
                showStatusMessage(tr("Executable failed: %1")
                    .arg(QString::fromLocal8Bit(msg)));
                shutdown();
                showMessageBox(QMessageBox::Critical,
                    tr("Executable failed"), QString::fromLocal8Bit(msg));
810
811
812
813
814
            } else {
                showMessageBox(QMessageBox::Critical,
                    tr("Executable failed"), QString::fromLocal8Bit(msg));
                showStatusMessage(tr("Executable failed: %1")
                    .arg(QString::fromLocal8Bit(msg)));
815
            }
816
817
818
819
        }
        return;
    }

820
    GdbCommand cmd = m_cookieForToken.take(token);
821
    if (theDebuggerBoolSetting(LogTimeStamps)) {
822
        gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
823
824
825
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
826

827
    if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
828
        //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
con's avatar
con committed
829
830
831
        return;
    }

832
    response->cookie = cmd.cookie;
hjk's avatar
hjk committed
833

834
835
836
837
838
    if (response->resultClass != GdbResultError &&
        response->resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
                                  (cmd.flags & ExitRequest) ? GdbResultExit :
                                  GdbResultDone)) {
        QString rsp = _(GdbResponse::stringFromResultClass(response->resultClass));
839
840
        qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
        debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
841
842
    } else {
        if (cmd.callback)
843
            (this->*cmd.callback)(*response);
844
        else if (cmd.adapterCallback)
845
            (m_gdbAdapter->*cmd.adapterCallback)(*response);
846
    }
con's avatar
con committed
847

848
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
849
        --m_pendingRequests;
850
851
        PENDING_DEBUG("   WATCH" << cmd.command << "=>" << cmd.callbackName
                      << "DECREMENTS PENDING TO" << m_pendingRequests);
852
        if (m_pendingRequests <= 0) {
853
            PENDING_DEBUG("\n\n ... AND TRIGGERS MODEL UPDATE\n");
hjk's avatar
hjk committed
854
            rebuildModel();
855
        }
con's avatar
con committed
856
    } else {
857
858
        PENDING_DEBUG("   OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
859
    }
860

861
862
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
863
864
865
866
    // 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.
867
    if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
868
        debugMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
869
870
        CommandsDoneCallback cont = m_commandsDoneCallback;
        m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
871
872
873
        (this->*cont)();
    } else {
        PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
874
    }
con's avatar
con committed
875
876
877
878
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
hjk's avatar
hjk committed
879
    if (state() == DebuggerNotReady) {
880
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
881
882
883
        return;
    }

hjk's avatar
hjk committed
884
    m_gdbAdapter->write(command.toLatin1() + "\r\n");
con's avatar
con committed
885
886
}

887
// Called from CoreAdapter and AttachAdapter
888
void GdbEngine::updateAll()
889
{
890
    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
891
    tryLoadDebuggingHelpers();
892
    reloadModulesInternal();
hjk's avatar
hjk committed
893
894
    postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames),
        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
895
    manager()->stackHandler()->setCurrentIndex(0);