gdbengine.cpp 155 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
104
#endif

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

hjk's avatar
hjk committed
107
static bool stateAcceptsGdbCommands(DebuggerState state)
hjk's avatar
hjk committed
108
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    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
132

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

139
// reads a MI-encoded item frome the consolestream
hjk's avatar
hjk committed
140
static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
141
{
hjk's avatar
hjk committed
142
    GdbMi output = response.data.findChild("consolestreamoutput");
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    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
162
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
163
{
hjk's avatar
hjk committed
164
    GdbMi output = response.data.findChild("consolestreamoutput");
165
166
167
168
169
170
171
172
173
174
    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
175
176
177
178
179
180
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

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

193
194
    connect(theDebuggerAction(AutoDerefPointers), SIGNAL(valueChanged(QVariant)),
            this, SLOT(setAutoDerefPointers(QVariant)));
195
}
196

197
void GdbEngine::connectDebuggingHelperActions()
198
{
199
200
201
202
203
204
    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()));
205
206
}
   
207
208
209
210
211
212
213
void GdbEngine::disconnectDebuggingHelperActions()
{
    disconnect(theDebuggerAction(UseDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(DebugDebuggingHelpers), 0, this, 0);
    disconnect(theDebuggerAction(RecheckDebuggingHelpers), 0, this, 0);
}

214
215
216
217
DebuggerStartMode GdbEngine::startMode() const
{
    QTC_ASSERT(!m_startParameters.isNull(), return NoStartMode);
    return m_startParameters->startMode;
con's avatar
con committed
218
219
}

220
221
222
223
224
QMainWindow *GdbEngine::mainWindow() const
{
    return m_manager->mainWindow();
}

con's avatar
con committed
225
226
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
227
    // prevent sending error messages afterwards
228
    disconnect(&m_gdbProc);
229
    delete m_gdbAdapter;
230
    m_gdbAdapter = 0;
hjk's avatar
hjk committed
231
232
}

233
void GdbEngine::connectAdapter()
con's avatar
con committed
234
{
hjk's avatar
hjk committed
235
236
    connect(m_gdbAdapter, SIGNAL(adapterStarted()),
        this, SLOT(handleAdapterStarted()));
237
238
    connect(m_gdbAdapter, SIGNAL(adapterStartFailed(QString,QString)),
        this, SLOT(handleAdapterStartFailed(QString,QString)));
hjk's avatar
hjk committed
239

240
241
242
    connect(m_gdbAdapter, SIGNAL(inferiorPrepared()),
        this, SLOT(handleInferiorPrepared()));

hjk's avatar
hjk committed
243
244
245
    connect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
        this, SLOT(handleInferiorStartFailed(QString)));

246
247
    connect(m_gdbAdapter, SIGNAL(adapterCrashed(QString)),
        this, SLOT(handleAdapterCrashed(QString)));
248
}
249

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

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

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

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

    m_inbuffer.clear();

276
277
278
279
    // ConverterState has no reset() function.
    m_outputCodecState.~ConverterState();
    new (&m_outputCodecState) QTextCodec::ConverterState();

280
281
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
282
    m_dumperHelper.clear();
283
284
}

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

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

328
329
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
330
    m_manager->showApplicationOutput(m_outputCodec->toUnicode(
331
332
333
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
334
335
void GdbEngine::debugMessage(const QString &msg)
{
336
    gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
337
338
}

339
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
340
341
342
{
    static QTime lastTime;

343
    if (theDebuggerBoolSetting(LogTimeStamps))
344
345
        gdbOutputAvailable(LogTime, currentTime());
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
346
347
348
349
350

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

    lastTime = QTime::currentTime();

360
361
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
362

363
364
365
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
366

367
368
369
370
    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
371
            break;
372
373
374
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
375
        //qDebug() << "found token" << token;
376
    }
con's avatar
con committed
377

hjk's avatar
hjk committed
378
    // next char decides kind of response
379
380
381
382
383
384
385
386
387
388
389
390
391
392
    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
393

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

476
        case '~': {
477
478
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
479
480
481
482
483
484
485

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

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

510
        case '@': {
511
            readDebugeeOutput(GdbMi::parseCString(from, to));
512
            break;
con's avatar
con committed
513
514
        }

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

525
        case '^': {
hjk's avatar
hjk committed
526
            GdbResponse response;
con's avatar
con committed
527

hjk's avatar
hjk committed
528
            response.token = token;
con's avatar
con committed
529

530
531
532
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
533

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

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

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
hjk's avatar
hjk committed
567
            response.data.setStreamOutput("logstreamoutput",
568
                m_pendingLogStreamOutput);
hjk's avatar
hjk committed
569
            response.data.setStreamOutput("consolestreamoutput",
570
571
572
573
                m_pendingConsoleStreamOutput);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

574
            handleResultRecord(&response);
575
576
577
578
579
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
580
581
582
583
584
585
        }
    }
}

void GdbEngine::readGdbStandardError()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
586
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
587
588
589
590
}

void GdbEngine::readGdbStandardOutput()
{
591
592
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
593

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
594
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
595

596
597
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
598
        int end = m_inbuffer.indexOf('\n', scan);
599
600
601
602
603
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
604
        scan = newstart;
605
606
        if (end == start)
            continue;
607
        #if defined(Q_OS_WIN)
608
609
610
611
612
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
613
        #endif
614
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
615
    }
616
    m_inbuffer.clear();
con's avatar
con committed
617
618
619
620
}

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

hjk's avatar
hjk committed
623
624
625
    setState(InferiorStopping);
    showStatusMessage(tr("Stop requested..."), 5000);

626
    debugMessage(_("TRYING TO INTERRUPT INFERIOR"));
hjk's avatar
hjk committed
627
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
628
629
630
631
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
632
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
633
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
634
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
635
636
        return;
    }
hjk's avatar
hjk committed
637
    if (pid == inferiorPid())
con's avatar
con committed
638
        return;
639
640
    debugMessage(_("FOUND PID %1").arg(pid));    

641
    handleInferiorPidChanged(pid);
642
643
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
644
645
}

hjk's avatar
hjk committed
646
647
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
648
649
650
651
652
653
654
{
    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
655
656
657
{
    GdbCommand cmd;
    cmd.command = command;
658
    cmd.flags = flags;
hjk's avatar
hjk committed
659
660
661
662
663
664
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

665
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
666
667
                            const char *callbackName, const QVariant &cookie)
{
668
    postCommand(command, NoFlags, callback, callbackName, cookie);
669
670
}

671
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
672
673
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
hjk's avatar
hjk committed
674
675
676
677
678
679
680
681
682
683
684
{
    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
685
{
hjk's avatar
hjk committed
686
    if (!stateAcceptsGdbCommands(state())) {
hjk's avatar
hjk committed
687
        PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + cmd.command);
688
689
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
            .arg(cmd.command).arg(state()));
con's avatar
con committed
690
691
692
        return;
    }

hjk's avatar
hjk committed
693
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
694
        ++m_pendingRequests;
hjk's avatar
hjk committed
695
        PENDING_DEBUG("   CALLBACK" << cmd.callbackName
696
            << "INCREMENTS PENDING TO:" << m_pendingRequests << cmd.command);
con's avatar
con committed
697
    } else {
hjk's avatar
hjk committed
698
        PENDING_DEBUG("   UNKNOWN CALLBACK" << cmd.callbackName
699
            << "LEAVES PENDING AT:" << m_pendingRequests << cmd.command);
con's avatar
con committed
700
701
    }

702
    if ((cmd.flags & NeedsStop) || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
703
704
        if (state() == InferiorStopped
            || state() == InferiorStarting || state() == AdapterStarted) {
hjk's avatar
hjk committed
705
706
707
708
709
710
            // 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);
711
712
713
714
            if (state() != InferiorStopping) {
                showStatusMessage(tr("Stopping temporarily."), 1000);
                interruptInferior(); // FIXME: race condition between gdb and kill()
            }
hjk's avatar
hjk committed
715
        }
hjk's avatar
hjk committed
716
    } else if (!cmd.command.isEmpty()) {
717
        flushCommand(cmd);
con's avatar
con committed
718
719
720
    }
}

hjk's avatar
hjk committed
721
void GdbEngine::flushCommand(const GdbCommand &cmd0)
722
{
hjk's avatar
hjk committed
723
    GdbCommand cmd = cmd0;
hjk's avatar
hjk committed
724
    if (state() == DebuggerNotReady) {
725
        gdbInputAvailable(LogInput, cmd.command);
726
727
728
729
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

730
    ++currentToken();
731
    cmd.postTime = QTime::currentTime();
732
733
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
734
    if (cmd.flags & EmbedToken)
735
        cmd.command = cmd.command.arg(currentToken());
736
    gdbInputAvailable(LogInput, cmd.command);
737
738

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

741
void GdbEngine::handleResultRecord(GdbResponse *response)
con's avatar
con committed
742
{
hjk's avatar
hjk committed
743
    //qDebug() << "TOKEN:" << response.token
744
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
hjk's avatar
hjk committed
745
    //qDebug() << "\nRESULT" << response.token << response.toString();
con's avatar
con committed
746

747
    int token = response->token;
con's avatar
con committed
748
749
750
    if (token == -1)
        return;

751
    if (!m_cookieForToken.contains(token)) {
752
753
754
755
        // 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.
756
757
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
758
759
        if (response->resultClass == GdbResultError) {
            QByteArray msg = response->data.findChild("msg").data();
760
761
762
763
764
765
766
            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."));
767
                shutdown();
768
769
770
            } 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.
771
772
                setState(InferiorStopping);
                setState(InferiorStopped);
773
774
775
776
777
778
779
780
781
782
783
            } 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)));
784
            }
785
786
787
788
        }
        return;
    }

789
    GdbCommand cmd = m_cookieForToken.take(token);
790
    if (theDebuggerBoolSetting(LogTimeStamps)) {
791
        gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
792
793
794
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
795

796
    if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
797
        //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
con's avatar
con committed
798
799
800
        return;
    }

801
    response->cookie = cmd.cookie;
hjk's avatar
hjk committed
802

803
804
805
806
807
    if (response->resultClass != GdbResultError &&
        response->resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
                                  (cmd.flags & ExitRequest) ? GdbResultExit :
                                  GdbResultDone)) {
        QString rsp = _(GdbResponse::stringFromResultClass(response->resultClass));
808
809
        qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
        debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
810
811
    } else {
        if (cmd.callback)
812
            (this->*cmd.callback)(*response);
813
        else if (cmd.adapterCallback)
814
            (m_gdbAdapter->*cmd.adapterCallback)(*response);
815
    }
con's avatar
con committed
816

817
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
818
        --m_pendingRequests;
hjk's avatar
hjk committed
819
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
820
            << m_pendingRequests << cmd.command);
821
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
822
823
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
824
        }
con's avatar
con committed
825
    } else {
hjk's avatar
hjk committed
826
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
827
828
            << m_pendingRequests << cmd.command);
    }
829

830
831
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
832
833
834
835
    // 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.
836
    if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
837
        debugMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
838
839
        CommandsDoneCallback cont = m_commandsDoneCallback;
        m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
840
841
842
        (this->*cont)();
    } else {
        PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
843
    }
con's avatar
con committed
844
845
846
847
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
hjk's avatar
hjk committed
848
    if (state() == DebuggerNotReady) {
849
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
850
851
852
        return;
    }

hjk's avatar
hjk committed
853
    m_gdbAdapter->write(command.toLatin1() + "\r\n");
con's avatar
con committed
854
855
}

856
// Called from CoreAdapter and AttachAdapter
857
void GdbEngine::updateAll()
858
{
859
    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
860
    tryLoadDebuggingHelpers();
hjk's avatar
hjk committed
861
862
    postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames),
        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
863
    manager()->stackHandler()->setCurrentIndex(0);
864
    if (supportsThreads())
865
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
hjk's avatar
hjk committed
866
    manager()->reloadRegisters();
hjk's avatar
hjk committed
867
    updateLocals(); 
868
869
}

hjk's avatar
hjk committed
870
void GdbEngine::handleQuerySources(const GdbResponse &response)
con's avatar
con committed
871
{
872
    m_sourcesListUpdating = false;
hjk's avatar
hjk committed
873
    if (response.resultClass == GdbResultDone) {
874
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
875
876
877
878
        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
879
        GdbMi files = response.data.findChild("files");
con's avatar
con committed
880
        foreach (const GdbMi &item, files.children()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
881
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
882
            GdbMi fullName = item.findChild("fullname");
883
884
885
886
887
888
889
890
891
            if (fullName.isValid()) {
                QString full = QString::fromLocal8Bit(fullName.data());
                if (QFileInfo(full).isReadable()) {
                    #ifdef Q_OS_WIN
                    full = QDir::cleanPath(full);
                    #endif
                    m_shortToFullName[fileName] = full;
                    m_fullToShortName[full] = fileName;
                }
con's avatar
con committed
892
893
            }
        }
hjk's avatar