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 "remotegdbadapter.h"
41
#include "trkgdbadapter.h"
con's avatar
con committed
42

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

con's avatar
con committed
224
225
GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
226
    // prevent sending error messages afterwards
227
    delete m_gdbAdapter;
hjk's avatar
hjk committed
228
229
}

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

    connect(m_gdbAdapter, SIGNAL(inferiorStartFailed(QString)),
        this, SLOT(handleInferiorStartFailed(QString)));

240
241
    connect(m_gdbAdapter, SIGNAL(adapterCrashed(QString)),
        this, SLOT(handleAdapterCrashed(QString)));
242
}
243

244
245
void GdbEngine::initializeVariables()
{
246
    m_debuggingHelperState = DebuggingHelperUninitialized;
247
    m_gdbVersion = 100;
hjk's avatar
hjk committed
248
    m_gdbBuildVersion = -1;
hjk's avatar
hjk committed
249
    m_isSynchroneous = false;
250
251
252
253
254
255
256
257
258

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
259
    m_commandsDoneCallback = 0;
260
    m_commandsToRunOnTemporaryBreak.clear();
261
    m_cookieForToken.clear();
262
263
264
265
266
267

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

    m_inbuffer.clear();

268
269
270
271
    // ConverterState has no reset() function.
    m_outputCodecState.~ConverterState();
    new (&m_outputCodecState) QTextCodec::ConverterState();

272
273
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
274
    m_dumperHelper.clear();
275
276
}

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

#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
308
    Q_UNUSED(to)
con's avatar
con committed
309
310
311
312
313
314
315
316
317
318
319
    // 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

320
321
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
322
    m_manager->showApplicationOutput(m_outputCodec->toUnicode(
323
324
325
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
326
327
void GdbEngine::debugMessage(const QString &msg)
{
328
    gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
329
330
}

331
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
332
333
334
{
    static QTime lastTime;

335
    if (theDebuggerBoolSetting(LogTimeStamps))
336
337
        gdbOutputAvailable(LogTime, currentTime());
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
338
339
340
341
342

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
343
344
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
345
        << "size:" << buff.size();
con's avatar
con committed
346
#else
347
    //qDebug() << "buf:" << buff;
con's avatar
con committed
348
349
350
351
#endif

    lastTime = QTime::currentTime();

352
353
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
354

355
356
357
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
358

359
360
361
362
    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
363
            break;
364
365
366
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
367
        //qDebug() << "found token" << token;
368
    }
con's avatar
con committed
369

hjk's avatar
hjk committed
370
    // next char decides kind of response
371
372
373
374
375
376
377
378
379
380
381
382
383
384
    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
385

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

464
        case '~': {
465
466
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
467
468
469
470
471
472
473

            // 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
474
475
476
                // Mac: [Switching to process 9294 local thread 0x2e03] or
                // [Switching to process 31773]
                static QRegExp re3(_("Switching to process ([0-9]+)"));
477
478
479
480
481
                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));
482
483
                else if (re3.indexIn(_(data)) != -1)
                    maybeHandleInferiorPidChanged(re3.cap(1));
484
            }
485
486

            // Show some messages to give the impression something happens.
hjk's avatar
hjk committed
487
            if (data.startsWith("Reading symbols from "))
488
                showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))), 1000);
489
490
491
            if (data.endsWith('\n'))
                data.chop(1);
            if (data.startsWith("[New ") || data.startsWith("[Thread "))
492
                showStatusMessage(_(data), 1000);
493
494
            break;
        }
495

496
        case '@': {
497
            readDebugeeOutput(GdbMi::parseCString(from, to));
498
            break;
con's avatar
con committed
499
500
        }

501
502
503
504
505
506
        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
507
                manager()->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
508
509
510
            break;
        }

511
        case '^': {
hjk's avatar
hjk committed
512
            GdbResponse response;
con's avatar
con committed
513

hjk's avatar
hjk committed
514
            response.token = token;
con's avatar
con committed
515

516
517
518
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
519

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
520
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
521
            if (resultClass == "done") {
hjk's avatar
hjk committed
522
                response.resultClass = GdbResultDone;
523
524
525
            } else if (resultClass == "running") {
                setState(InferiorRunning);
                showStatusMessage(tr("Running..."));
hjk's avatar
hjk committed
526
                response.resultClass = GdbResultRunning;
527
            } else if (resultClass == "connected") {
hjk's avatar
hjk committed
528
                response.resultClass = GdbResultConnected;
529
            } else if (resultClass == "error") {
hjk's avatar
hjk committed
530
                response.resultClass = GdbResultError;
531
            } else if (resultClass == "exit") {
hjk's avatar
hjk committed
532
                response.resultClass = GdbResultExit;
533
            } else {
hjk's avatar
hjk committed
534
                response.resultClass = GdbResultUnknown;
535
            }
con's avatar
con committed
536

537
538
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
539
540
                if (*from == ',') {
                    ++from;
hjk's avatar
hjk committed
541
542
543
                    response.data.parseTuple_helper(from, to);
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
hjk's avatar
hjk committed
544
545
                } else {
                    // Archer has this
hjk's avatar
hjk committed
546
547
                    response.data.m_type = GdbMi::Tuple;
                    response.data.m_name = "data";
con's avatar
con committed
548
549
                }
            }
550
551
552

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
hjk's avatar
hjk committed
553
            response.data.setStreamOutput("logstreamoutput",
554
                m_pendingLogStreamOutput);
hjk's avatar
hjk committed
555
            response.data.setStreamOutput("consolestreamoutput",
556
557
558
559
                m_pendingConsoleStreamOutput);
            m_pendingLogStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

hjk's avatar
hjk committed
560
            handleResultRecord(response);
561
562
563
564
565
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
566
567
568
569
570
571
        }
    }
}

void GdbEngine::readGdbStandardError()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
572
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
573
574
575
576
}

void GdbEngine::readGdbStandardOutput()
{
577
578
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
579

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
580
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
581

582
583
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
584
        int end = m_inbuffer.indexOf('\n', scan);
585
586
587
588
589
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
590
        scan = newstart;
591
592
        if (end == start)
            continue;
593
        #if defined(Q_OS_WIN)
594
595
596
597
598
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
599
        #endif
600
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
601
    }
602
    m_inbuffer.clear();
con's avatar
con committed
603
604
605
606
}

void GdbEngine::interruptInferior()
{
hjk's avatar
hjk committed
607
    QTC_ASSERT(state() == InferiorRunning, qDebug() << state());
608

hjk's avatar
hjk committed
609
    if (state() == DebuggerNotReady) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
610
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
611
        shutdown();
con's avatar
con committed
612
        return;
613
    }
con's avatar
con committed
614

hjk's avatar
hjk committed
615
616
617
    setState(InferiorStopping);
    showStatusMessage(tr("Stop requested..."), 5000);

618
    debugMessage(_("TRYING TO INTERUPT INFERIOR"));
hjk's avatar
hjk committed
619
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
620
621
622
623
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
624
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
625
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
626
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
627
628
        return;
    }
hjk's avatar
hjk committed
629
    if (pid == inferiorPid())
con's avatar
con committed
630
        return;
631
632
    debugMessage(_("FOUND PID %1").arg(pid));    

633
    handleInferiorPidChanged(pid);
634
635
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
636
637
}

hjk's avatar
hjk committed
638
639
void GdbEngine::postCommand(const QString &command, AdapterCallback callback,
                            const char *callbackName, const QVariant &cookie)
640
641
642
643
644
645
646
{
    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
647
648
649
{
    GdbCommand cmd;
    cmd.command = command;
650
    cmd.flags = flags;
hjk's avatar
hjk committed
651
652
653
654
655
656
    cmd.adapterCallback = callback;
    cmd.callbackName = callbackName;
    cmd.cookie = cookie;
    postCommandHelper(cmd);
}

657
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
658
659
                            const char *callbackName, const QVariant &cookie)
{
660
    postCommand(command, NoFlags, callback, callbackName, cookie);
661
662
}

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

hjk's avatar
hjk committed
685
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
686
        ++m_pendingRequests;
hjk's avatar
hjk committed
687
        PENDING_DEBUG("   CALLBACK" << cmd.callbackName
688
            << "INCREMENTS PENDING TO:" << m_pendingRequests << cmd.command);
con's avatar
con committed
689
    } else {
hjk's avatar
hjk committed
690
        PENDING_DEBUG("   UNKNOWN CALLBACK" << cmd.callbackName
691
            << "LEAVES PENDING AT:" << m_pendingRequests << cmd.command);
con's avatar
con committed
692
693
    }

hjk's avatar
hjk committed
694
    if (cmd.flags & NeedsStop) {
695
        if (state() == InferiorStopped || state() == AdapterStarted) {
hjk's avatar
hjk committed
696
697
698
699
700
701
702
703
704
705
            // Can be safely sent now.
            flushCommand(cmd);
        } else {
            // Queue the commands that we cannot send at once.
            showStatusMessage(tr("Stopping temporarily."), 1000);
            qDebug() << _("QUEUING COMMAND ") + cmd.command;
            debugMessage(_("QUEUING COMMAND ") + cmd.command);
            m_commandsToRunOnTemporaryBreak.append(cmd);
            interruptInferior();
        }
hjk's avatar
hjk committed
706
    } else if (!cmd.command.isEmpty()) {
707
        flushCommand(cmd);
con's avatar
con committed
708
709
710
    }
}

hjk's avatar
hjk committed
711
void GdbEngine::flushCommand(const GdbCommand &cmd0)
712
{
hjk's avatar
hjk committed
713
    GdbCommand cmd = cmd0;
hjk's avatar
hjk committed
714
    if (state() == DebuggerNotReady) {
715
        gdbInputAvailable(LogInput, cmd.command);
716
717
718
719
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

720
    ++currentToken();
721
    cmd.postTime = QTime::currentTime();
722
723
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
724
    if (cmd.flags & EmbedToken)
725
        cmd.command = cmd.command.arg(currentToken());
726
    gdbInputAvailable(LogInput, cmd.command);
727
728

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

hjk's avatar
hjk committed
731
void GdbEngine::handleResultRecord(const GdbResponse &response)
con's avatar
con committed
732
{
hjk's avatar
hjk committed
733
    //qDebug() << "TOKEN:" << response.token
734
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
hjk's avatar
hjk committed
735
    //qDebug() << "\nRESULT" << response.token << response.toString();
con's avatar
con committed
736

hjk's avatar
hjk committed
737
    int token = response.token;
con's avatar
con committed
738
739
740
    if (token == -1)
        return;

741
    if (!m_cookieForToken.contains(token)) {
742
743
744
745
        // 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.
746
747
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
hjk's avatar
hjk committed
748
749
        if (response.resultClass == GdbResultError) {
            QByteArray msg = response.data.findChild("msg").data();
750
751
752
753
754
755
756
            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."));
757
                shutdown();
758
759
760
            } 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.
761
762
                setState(InferiorStopping);
                setState(InferiorStopped);
763
764
765
766
767
768
769
770
771
772
773
            } 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)));
774
            }
775
776
777
778
        }
        return;
    }

779
    GdbCommand cmd = m_cookieForToken.take(token);
780
    if (theDebuggerBoolSetting(LogTimeStamps)) {
781
        gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
782
783
784
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
785

hjk's avatar
hjk committed
786
    if (response.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
787
        //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
con's avatar
con committed
788
789
790
        return;
    }

hjk's avatar
hjk committed
791
792
793
    GdbResponse responseWithCookie = response;
    responseWithCookie.cookie = cmd.cookie;

794
    if (response.resultClass != GdbResultError &&
795
796
797
798
799
800
        response.resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
                                 (cmd.flags & ExitRequest) ? GdbResultExit :
                                 GdbResultDone)) {
        QString rsp = _(GdbResponse::stringFromResultClass(response.resultClass));
        qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
        debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
801
802
803
804
805
806
    } else {
        if (cmd.callback)
            (this->*cmd.callback)(responseWithCookie);
        else if (cmd.adapterCallback)
            (m_gdbAdapter->*cmd.adapterCallback)(responseWithCookie);
    }
con's avatar
con committed
807

808
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
809
        --m_pendingRequests;
hjk's avatar
hjk committed
810
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
811
            << m_pendingRequests << cmd.command);
812
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
813
814
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
815
        }
con's avatar
con committed
816
    } else {
hjk's avatar
hjk committed
817
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
818
819
            << m_pendingRequests << cmd.command);
    }
820

821
822
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
823
824
825
826
    // 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.
827
    if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
828
        debugMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
829
830
        CommandsDoneCallback cont = m_commandsDoneCallback;
        m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
831
832
833
        (this->*cont)();
    } else {
        PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
834
    }
con's avatar
con committed
835
836
837
838
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
hjk's avatar
hjk committed
839
    if (state() == DebuggerNotReady) {
840
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
841
842
843
        return;
    }

hjk's avatar
hjk committed
844
    m_gdbAdapter->write(command.toLatin1() + "\r\n");
con's avatar
con committed
845
846
}

847
// Called from CoreAdapter and AttachAdapter
848
void GdbEngine::updateAll()
849
{
850
    QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopped, /**/);
851
    tryLoadDebuggingHelpers();
hjk's avatar
hjk committed
852
853
    postCommand(_("-stack-list-frames"), WatchUpdate, CB(handleStackListFrames),
        QVariant::fromValue<StackCookie>(StackCookie(false, true)));
854
    manager()->stackHandler()->setCurrentIndex(0);
855
    if (supportsThreads())
856
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
hjk's avatar
hjk committed
857
    manager()->reloadRegisters();
hjk's avatar
hjk committed
858
    updateLocals(); 
859
860
}

hjk's avatar
hjk committed
861
void GdbEngine::handleQuerySources(const GdbResponse &response)
con's avatar
con committed
862
{
hjk's avatar
hjk committed
863
    if (response.resultClass == GdbResultDone) {
864
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
865
866
867
868
        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
869
        GdbMi files = response.data.findChild("files");
con's avatar
con committed
870
        foreach (const GdbMi &item, files.children()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
871
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
872
            GdbMi fullName = item.findChild("fullname");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
873
            QString full = QString::fromLocal8Bit(fullName.data());
con's avatar
con committed
874
875
876
877
878
879
880
881
            #ifdef Q_OS_WIN
            full = QDir::cleanPath(full);
            #endif
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
                m_shortToFullName[fileName] = full;
                m_fullToShortName[full] = fileName;
            }
        }
882
        if (m_shortToFullName != oldShortToFull)
hjk's avatar
hjk committed
883
            manager()->sourceFileWindow()->setSourceFiles(m_shortToFullName);
con's avatar
con committed
884
885
886
    }
}

887
#if 0
hjk's avatar
hjk committed
888
void GdbEngine::handleExecJumpToLine(const GdbResponse &response)
con's avatar
con committed
889
890
891
892
893
894
895
896
{
    // FIXME: remove this special case as soon as 'jump'
    // is supported by MI
    // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
    // ~"Continuing at 0x4058f3."
    // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
    // ~"242\t x *= 2;"
    //109^done"
hjk's avatar
hjk committed
897
    setState(InferiorStopped);
hjk's avatar
hjk committed
898
    showStatusMessage(tr("Jumped. Stopped."));
hjk's avatar
hjk committed
899
    QByteArray output = response.data.findChild("logstreamoutput").data();
900
    if (output.isEmpty())
con's avatar
con committed
901
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
902
903
904
905
906
907
    int idx1 = output.indexOf(' ') + 1;
    if (idx1 > 0) {
        int idx2 = output.indexOf(':', idx1);
        if (idx2 > 0) {
            QString file = QString::fromLocal8Bit(output.mid(idx1, idx2 - idx1));
            int line = output.mid(idx2 + 1).toInt();
hjk's avatar
hjk committed
908
            gotoLocation(file, line, true);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
909
910
        }
    }
con's avatar
con committed
911
}
912
#endif
con's avatar
con committed
913

914