gdbengine.cpp 142 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
26
** contact the sales department at http://www.qtsoftware.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"
con's avatar
con committed
34

35
#include "watchutils.h"
36
#include "debuggeractions.h"
con's avatar
con committed
37
38
#include "debuggerconstants.h"
#include "debuggermanager.h"
39
#include "debuggertooltip.h"
con's avatar
con committed
40
41
42
43
44
45
46
47
48
#include "gdbmi.h"
#include "procinterrupt.h"

#include "disassemblerhandler.h"
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
49
#include "sourcefileswindow.h"
con's avatar
con committed
50

51
#include "debuggerdialogs.h"
con's avatar
con committed
52

hjk's avatar
hjk committed
53
#include <utils/qtcassert.h>
54
#include <texteditor/itexteditor.h>
55
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
56

con's avatar
con committed
57
58
59
60
61
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTime>
#include <QtCore/QTimer>
62
#include <QtCore/QTextStream>
con's avatar
con committed
63
64

#include <QtGui/QAction>
65
#include <QtGui/QApplication>
con's avatar
con committed
66
67
68
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
69
70
#include <QtGui/QDialogButtonBox>
#include <QtGui/QPushButton>
71
#ifdef Q_OS_WIN
72
#    include "shared/sharedlibraryinjector.h"
73
#endif
con's avatar
con committed
74

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
75
#ifdef Q_OS_UNIX
con's avatar
con committed
76
77
78
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
79
#include <ctype.h>
con's avatar
con committed
80
81
82
83
84
85
86
87
88
89
90
91
92

using namespace Debugger;
using namespace Debugger::Internal;
using namespace Debugger::Constants;

Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);

//#define DEBUG_PENDING  1
//#define DEBUG_SUBITEM  1

#if DEBUG_PENDING
#   define PENDING_DEBUG(s) qDebug() << s
#else
Roberto Raggi's avatar
Roberto Raggi committed
93
#   define PENDING_DEBUG(s)
con's avatar
con committed
94
95
#endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
96
97
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
98
99
#define CB(callback) &GdbEngine::callback, STRINGIFY(callback)

con's avatar
con committed
100
101
102
103
104
105
106
107
108
109
110
111
static int &currentToken()
{
    static int token = 0;
    return token;
}

///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

112
113
GdbEngine::GdbEngine(DebuggerManager *parent) :
#ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
114
    m_dumperInjectionLoad(true),
115
#else
116
    m_dumperInjectionLoad(false),
117
#endif
118
119
    q(parent),
    qq(parent->engineInterface())
con's avatar
con committed
120
{
121
    m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
122
123
124
#ifdef Q_OS_UNIX
    m_stubProc.setSettings(Core::ICore::instance()->settings());
#endif
125
126
    initializeVariables();
    initializeConnections();
con's avatar
con committed
127
128
129
130
}

GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
131
132
    // prevent sending error messages afterwards
    m_gdbProc.disconnect(this);
con's avatar
con committed
133
134
}

135
void GdbEngine::initializeConnections()
con's avatar
con committed
136
137
{
    // Gdb Process interaction
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(gdbProcError(QProcess::ProcessError)));
    connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readGdbStandardOutput()));
    connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readGdbStandardError()));
    connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
        q, SLOT(exitDebugger()));

    connect(&m_stubProc, SIGNAL(processError(QString)),
        this, SLOT(stubError(QString)));
    connect(&m_stubProc, SIGNAL(processStarted()),
        this, SLOT(stubStarted()));
    connect(&m_stubProc, SIGNAL(wrapperStopped()),
        q, SLOT(exitDebugger()));

    connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(uploadProcError(QProcess::ProcessError)));
156
157
158
159
    connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readUploadStandardOutput()));
    connect(&m_uploadProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readUploadStandardError()));
160

con's avatar
con committed
161
    // Output
162
    connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
163
        this, SLOT(readDebugeeOutput(QByteArray)));
con's avatar
con committed
164

165
166
    connect(this, SIGNAL(gdbOutputAvailable(int,QString)),
        q, SLOT(showDebuggerOutput(int,QString)),
con's avatar
con committed
167
        Qt::QueuedConnection);
168
169
    connect(this, SIGNAL(gdbInputAvailable(int,QString)),
        q, SLOT(showDebuggerInput(int,QString)),
con's avatar
con committed
170
        Qt::QueuedConnection);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
171
172
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        q, SLOT(showApplicationOutput(QString)),
con's avatar
con committed
173
        Qt::QueuedConnection);
174

175
    // FIXME: These trigger even if the engine is not active
176
177
178
179
180
181
    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()));
con's avatar
con committed
182
183
}

184
185
void GdbEngine::initializeVariables()
{
186
    m_debuggingHelperState = DebuggingHelperUninitialized;
187
    m_gdbVersion = 100;
hjk's avatar
hjk committed
188
    m_gdbBuildVersion = -1;
189
190
191
192
193
194
195
196
197

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
198
    m_autoContinue = false;
199
    m_waitingForFirstBreakpointToBeHit = false;
200
    m_commandsToRunOnTemporaryBreak.clear();
201
    m_cookieForToken.clear();
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
    m_customOutputForToken.clear();

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

    m_inbuffer.clear();

    m_address.clear();
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
    m_dumperHelper = QtDumperHelper();

    // FIXME: unhandled:
    //m_outputCodecState = QTextCodec::ConverterState();
    //OutputCollector m_outputCollector;
    //QProcess m_gdbProc;
    //QProcess m_uploadProc;
    //Core::Utils::ConsoleProcess m_stubProc;
221
222
}

con's avatar
con committed
223
224
225
void GdbEngine::gdbProcError(QProcess::ProcessError error)
{
    QString msg;
226
    bool kill = true;
con's avatar
con committed
227
228
    switch (error) {
        case QProcess::FailedToStart:
229
            kill = false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
230
            msg = tr("The Gdb process failed to start. Either the "
con's avatar
con committed
231
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
232
                "permissions to invoke the program.")
hjk's avatar
hjk committed
233
                .arg(theDebuggerStringSetting(GdbLocation));
con's avatar
con committed
234
235
            break;
        case QProcess::Crashed:
236
            kill = false;
con's avatar
con committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
            msg = tr("The Gdb process crashed some time after starting "
                "successfully.");
            break;
        case QProcess::Timedout:
            msg = tr("The last waitFor...() function timed out. "
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
            break;
        case QProcess::WriteError:
            msg = tr("An error occurred when attempting to write "
                "to the Gdb process. For example, the process may not be running, "
                "or it may have closed its input channel.");
            break;
        case QProcess::ReadError:
            msg = tr("An error occurred when attempting to read from "
                "the Gdb process. For example, the process may not be running.");
            break;
        default:
            msg = tr("An unknown error in the Gdb process occurred. "
                "This is the default return value of error().");
    }

259
    q->showStatusMessage(msg);
con's avatar
con committed
260
261
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
    // act as if it was closed by the core
262
263
    if (kill)
        q->exitDebugger();
con's avatar
con committed
264
265
}

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
void GdbEngine::uploadProcError(QProcess::ProcessError error)
{
    QString msg;
    switch (error) {
        case QProcess::FailedToStart:
            msg = tr("The upload process failed to start. Either the "
                "invoked script '%1' is missing, or you may have insufficient "
                "permissions to invoke the program.")
                .arg(theDebuggerStringSetting(GdbLocation));
            break;
        case QProcess::Crashed:
            msg = tr("The upload process crashed some time after starting "
                "successfully.");
            break;
        case QProcess::Timedout:
            msg = tr("The last waitFor...() function timed out. "
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
            break;
        case QProcess::WriteError:
            msg = tr("An error occurred when attempting to write "
                "to the upload process. For example, the process may not be running, "
                "or it may have closed its input channel.");
            break;
        case QProcess::ReadError:
            msg = tr("An error occurred when attempting to read from "
                "the upload process. For example, the process may not be running.");
            break;
        default:
            msg = tr("An unknown error in the upload process occurred. "
                "This is the default return value of error().");
    }

    q->showStatusMessage(msg);
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
}

303
304
305
void GdbEngine::readUploadStandardOutput()
{
    QByteArray ba = m_uploadProc.readAllStandardOutput();
306
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(ba, ba.length()));
307
308
309
310
311
}

void GdbEngine::readUploadStandardError()
{
    QByteArray ba = m_uploadProc.readAllStandardError();
312
    gdbOutputAvailable(LogError, QString::fromLocal8Bit(ba, ba.length()));
313
314
}

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

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

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

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

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

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

    lastTime = QTime::currentTime();

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

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

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

381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
    // next char decides kind of record
    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
396

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

472
        case '~': {
473
474
475
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
            if (data.startsWith("Reading symbols from ")) {
476
                q->showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))));
477
            }
478
479
            break;
        }
480

481
        case '@': {
482
483
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingTargetStreamOutput += data;
484
            break;
con's avatar
con committed
485
486
        }

487
488
489
490
491
492
        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:"))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
493
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
494
495
496
            break;
        }

497
498
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
499

500
            record.token = token;
con's avatar
con committed
501

502
503
504
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
505

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
506
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
507
508
509
510
511
512
513
514
515
516
517
518
            if (resultClass == "done")
                record.resultClass = GdbResultDone;
            else if (resultClass == "running")
                record.resultClass = GdbResultRunning;
            else if (resultClass == "connected")
                record.resultClass = GdbResultConnected;
            else if (resultClass == "error")
                record.resultClass = GdbResultError;
            else if (resultClass == "exit")
                record.resultClass = GdbResultExit;
            else
                record.resultClass = GdbResultUnknown;
con's avatar
con committed
519

520
521
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
522
523
524
525
526
527
528
529
530
                if (*from == ',') {
                    ++from;
                    record.data.parseTuple_helper(from, to);
                    record.data.m_type = GdbMi::Tuple;
                    record.data.m_name = "data";
                } else {
                    // Archer has this
                    record.data.m_type = GdbMi::Tuple;
                    record.data.m_name = "data";
con's avatar
con committed
531
532
                }
            }
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
            record.data.setStreamOutput("logstreamoutput",
                m_pendingLogStreamOutput);
            record.data.setStreamOutput("targetstreamoutput",
                m_pendingTargetStreamOutput);
            record.data.setStreamOutput("consolestreamoutput",
                m_pendingConsoleStreamOutput);
            QByteArray custom = m_customOutputForToken[token];
            if (!custom.isEmpty())
                record.data.setStreamOutput("customvaluecontents",
                    '{' + custom + '}');
            //m_customOutputForToken.remove(token);
            m_pendingLogStreamOutput.clear();
            m_pendingTargetStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

            handleResultRecord(record);
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
558
559
560
561
        }
    }
}

562
void GdbEngine::handleStubAttached(const GdbResultRecord &, const QVariant &)
563
564
565
{
    qq->notifyInferiorStopped();
    handleAqcuiredInferior();
566
    m_autoContinue = true;
567
568
569
570
}

void GdbEngine::stubStarted()
{
571
572
573
    const qint64 attachedPID = m_stubProc.applicationPID();
    qq->notifyInferiorPidChanged(attachedPID);
    postCommand(_("attach %1").arg(attachedPID), CB(handleStubAttached));
574
575
576
577
578
579
580
}

void GdbEngine::stubError(const QString &msg)
{
    QMessageBox::critical(q->mainWindow(), tr("Debugger Error"), msg);
}

con's avatar
con committed
581
582
void GdbEngine::readGdbStandardError()
{
583
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
584
585
586
587
}

void GdbEngine::readGdbStandardOutput()
{
588
589
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
590

591
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
592

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

void GdbEngine::interruptInferior()
{
618
    qq->notifyInferiorStopRequested();
619

620
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
621
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
622
        qq->notifyInferiorExited();
con's avatar
con committed
623
        return;
624
    }
con's avatar
con committed
625

626
    if (q->startMode() == StartRemote) {
627
        postCommand(_("-exec-interrupt"));
628
629
630
        return;
    }

631
632
    const qint64 attachedPID = q->inferiorPid();
    if (attachedPID <= 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
633
        debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
con's avatar
con committed
634
635
636
        return;
    }

637
638
    if (!interruptProcess(attachedPID))
        debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
con's avatar
con committed
639
640
641
642
}

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

Roberto Raggi's avatar
Roberto Raggi committed
652
    qq->notifyInferiorPidChanged(pid);
653
654
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
655
656
}

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)
con's avatar
con committed
666
667
{
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
668
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + command);
con's avatar
con committed
669
670
671
        return;
    }

672
    if (flags & RebuildModel) {
con's avatar
con committed
673
        ++m_pendingRequests;
674
        PENDING_DEBUG("   CALLBACK" << callbackName << "INCREMENTS PENDING TO:"
con's avatar
con committed
675
676
            << m_pendingRequests << command);
    } else {
677
        PENDING_DEBUG("   UNKNOWN CALLBACK" << callbackName << "LEAVES PENDING AT:"
con's avatar
con committed
678
679
680
            << m_pendingRequests << command);
    }

681
    GdbCommand cmd;
con's avatar
con committed
682
    cmd.command = command;
683
684
685
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
con's avatar
con committed
686
687
    cmd.cookie = cookie;

688
    if ((flags & NeedsStop) && q->status() != DebuggerInferiorStopped
689
690
691
            && q->status() != DebuggerProcessStartingUp) {
        // queue the commands that we cannot send at once
        QTC_ASSERT(q->status() == DebuggerInferiorRunning,
692
            qDebug() << "STATUS:" << q->status());
693
        q->showStatusMessage(tr("Stopping temporarily."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
694
        debugMessage(_("QUEUING COMMAND ") + cmd.command);
695
696
697
        m_commandsToRunOnTemporaryBreak.append(cmd);
        interruptInferior();
    } else if (!command.isEmpty()) {
698
        flushCommand(cmd);
con's avatar
con committed
699
700
701
    }
}

702
703
704
705
706
void GdbEngine::flushCommand(GdbCommand &cmd)
{
    ++currentToken();
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
707
    if (cmd.flags & EmbedToken)
708
709
710
711
712
        cmd.command = cmd.command.arg(currentToken());

    m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
    //emit gdbInputAvailable(QString(), "         " +  currentTime());
    //emit gdbInputAvailable(QString(), "[" + currentTime() + "]    " + cmd.command);
713
    emit gdbInputAvailable(LogInput, cmd.command);
714
715
}

con's avatar
con committed
716
717
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
718
719
    //qDebug() << "TOKEN:" << record.token
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
con's avatar
con committed
720
721
722
723
724
725
726
    //qDebug() << "";
    //qDebug() << "\nRESULT" << record.token << record.toString();

    int token = record.token;
    if (token == -1)
        return;

727
    GdbCommand cmd = m_cookieForToken.take(token);
con's avatar
con committed
728

729
    if (record.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
730
        //qDebug() << "### SKIPPING OLD RESULT" << record.toString();
con's avatar
con committed
731
732
733
734
735
        //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
        return;
    }

#if 0
736
    qDebug() << "# handleOutput,"
hjk's avatar
hjk committed
737
        << "cmd name:" << cmd.callbackName
738
        << " cmd synchronized:" << cmd.synchronized
con's avatar
con committed
739
740
741
742
743
        << "\n record: " << record.toString();
#endif

    // << "\n data: " << record.data.toString(true);

744
745
    if (cmd.callback)
        (this->*(cmd.callback))(record, cmd.cookie);
con's avatar
con committed
746

747
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
748
        --m_pendingRequests;
hjk's avatar
hjk committed
749
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
750
            << m_pendingRequests << cmd.command);
751
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
752
753
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
754
        }
con's avatar
con committed
755
    } else {
hjk's avatar
hjk committed
756
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
757
758
            << m_pendingRequests << cmd.command);
    }
759
760
761
762
763
764
765
766
767
768

    // 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.
    if (m_cookieForToken.isEmpty() && m_autoContinue) {
        m_autoContinue = false;
        continueInferior();
        q->showStatusMessage(tr("Continuing after temporary stop."));
    }
con's avatar
con committed
769
770
771
772
773
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
774
        debugMessage(_("NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
775
776
777
        return;
    }

778
    m_gdbProc.write(command.toLocal8Bit() + "\r\n");
con's avatar
con committed
779
780
}

781
void GdbEngine::handleTargetCore(const GdbResultRecord &, const QVariant &)
782
783
{
    qq->notifyInferiorStopped();
784
785
    q->showStatusMessage(tr("Core file loaded."));
    q->resetLocation();
786
    tryLoadDebuggingHelpers();
787
788
    qq->stackHandler()->setCurrentIndex(0);
    updateLocals(); // Quick shot
789
    reloadStack();
790
    if (supportsThreads())
791
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
792
    qq->reloadRegisters();
793
794
}

795
void GdbEngine::handleQuerySources(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
796
797
{
    if (record.resultClass == GdbResultDone) {
798
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
799
800
801
802
803
804
        m_shortToFullName.clear();
        m_fullToShortName.clear();
        // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
        // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
        GdbMi files = record.data.findChild("files");
        foreach (const GdbMi &item, files.children()) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
805
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
806
            GdbMi fullName = item.findChild("fullname");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
807
            QString full = QString::fromLocal8Bit(fullName.data());
con's avatar
con committed
808
809
810
811
            #ifdef Q_OS_WIN
            full = QDir::cleanPath(full);
            #endif
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
812
                //qDebug() << "STORING 2:" << fileName << full;
con's avatar
con committed
813
814
815
816
                m_shortToFullName[fileName] = full;
                m_fullToShortName[full] = fileName;
            }
        }
817
818
        if (m_shortToFullName != oldShortToFull)
            qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
con's avatar
con committed
819
820
821
    }
}

822
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
823
{
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
    if (record.resultClass != GdbResultDone)
        return;
    // FIXME: use something more robust
    // WIN:     [New thread 3380.0x2bc]
    //          * 3 Thread 2312.0x4d0  0x7c91120f in ?? ()
    // LINUX:   * 1 Thread 0x7f466273c6f0 (LWP 21455)  0x0000000000404542 in ...
    const QString data = _(record.data.findChild("consolestreamoutput").data());
    if (data.isEmpty())
        return;
    // check "[New thread 3380.0x2bc]"
    if (data.startsWith(QLatin1Char('['))) {
        QRegExp ren(_("^\\[New thread (\\d+)\\.0x.*"));
        Q_ASSERT(ren.isValid());
        if (ren.indexIn(data) != -1) {
            maybeHandleInferiorPidChanged(ren.cap(1));
            return;
        }
841
    }
842
843
844
845
846
    // check "* 3 Thread ..."
    QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
    Q_ASSERT(re.isValid());
    if (re.indexIn(data) != -1)
        maybeHandleInferiorPidChanged(re.cap(1));
847
848
}

849
void GdbEngine::handleInfoProc(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
850
851
{
    if (record.resultClass == GdbResultDone) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
852
        #ifdef Q_OS_MAC
con's avatar
con committed
853
        //^done,process-id="85075"
Oswald Buddenhagen's avatar
nicer    
Oswald Buddenhagen committed
854
        maybeHandleInferiorPidChanged(_(record.data.findChild("process-id").data()));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
855
        #else
con's avatar
con committed
856
        // FIXME: use something more robust
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
857
858
        QRegExp re(__("process (\\d+)"));
        QString data = __(record.data.findChild("consolestreamoutput").data());
con's avatar
con committed
859
860
861
862
863
864
        if (re.indexIn(data) != -1)
            maybeHandleInferiorPidChanged(re.cap(1));
        #endif
    }
}

865
void GdbEngine::handleInfoShared(const GdbResultRecord &record, const QVariant &cookie)
con's avatar
con committed
866
867
868
{
    if (record.resultClass == GdbResultDone) {
        // let the modules handler do the parsing
869
        handleModulesList(record, cookie);
con's avatar
con committed
870
871
872
    }
}

873
#if 0
con's avatar
con committed
874
875
876
877
878
879
880
881
882
883
void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
{
    // 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"
    qq->notifyInferiorStopped();
884
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
885
    QByteArray output = record.data.findChild("logstreamoutput").data();
886
    if (output.isEmpty())
con's avatar
con committed
887
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
888
889
890
891
892
893
894
895
896
    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();
            q->gotoLocation(file, line, true);
        }
    }
con's avatar
con committed
897
}
898
#endif
con's avatar
con committed
899

900
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
901
902
903
904
905
906
907
908
{
    // FIXME: remove this special case as soon as there's a real
    // reason given when the temporary breakpoint is hit.
    // reight now we get:
    // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
    // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
    // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
    qq->notifyInferiorStopped();
909
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
con's avatar
con committed
910
    GdbMi frame = record.data.findChild("frame");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
911
    QString file = QString::fromLocal8Bit(frame.findChild("fullname").data());
con's avatar
con committed
912
    int line = frame.findChild("line").data().toInt();
913
914
    qDebug() << "HIT:" << file << line << "IN" << frame.toString()
        << "--" << record.toString();
con's avatar
con committed
915
916
917
    q->gotoLocation(file, line, true);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
918
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
919
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
920
921
922
923
    return reason == "exited-normally"   // inferior exited normally
        || reason == "exited-signalled"  // inferior exited because of a signal
        //|| reason == "signal-received" // inferior received signal
        || reason == "exited";           // inferior exited
con's avatar
con committed
924
925
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
926
static bool isStoppedReason(const QByteArray &reason)
con's avatar
con committed
927
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
928
929
930
931
932
933
934
    return reason == "function-finished"  // -exec-finish
        || reason == "signal-received"  // handled as "isExitedReason"
        || reason == "breakpoint-hit"     // -exec-continue
        || reason == "end-stepping-range" // -exec-next, -exec-step
        || reason == "location-reached"   // -exec-until
        || reason == "access-watchpoint-trigger"
        || reason == "read-watchpoint-trigger"
935
        #ifdef Q_OS_MAC
con's avatar
con committed
936
        || reason.isEmpty()
937
        #endif
con's avatar
con committed
938
939
940
    ;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
941
942
void GdbEngine::handleAqcuiredInferior()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
943
    #ifdef Q_OS_WIN
944
    postCommand(_("info thread"), CB(handleInfoThreads));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
945
    #elif defined(Q_OS_MAC)
946
    postCommand(_("info pid"), NeedsStop, CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
947
948
    #else
    postCommand(_("info proc"), CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
949
    #endif
950
    if (theDebuggerBoolSetting(ListSourceFiles))
951
        reloadSourceFiles();
952

953
    // Reverse debugging. FIXME: Should only be used when available.
954
955
    //if (theDebuggerBoolSetting(EnableReverseDebugging))
    //    postCommand(_("target record"));
956

957
    tryLoadDebuggingHelpers();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
958

959
    #ifndef Q_OS_MAC
960
    // intentionally after tryLoadDebuggingHelpers(),
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
961
    // otherwise we'd interupt solib loading.
hjk's avatar
hjk committed
962
    if (theDebuggerBoolSetting(AllPluginBreakpoints)) {
963
964
965
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 0"));
        postCommand(_("sharedlibrary .*"));
hjk's avatar
hjk committed
966
    } else if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
967
968
969
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 1"));
        postCommand(_("sharedlibrary ")
hjk's avatar
hjk committed
970
971
          + theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
    } else if (theDebuggerBoolSetting(NoPluginBreakpoints)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
972
        // should be like that already
973
974
        if (!m_dumperInjectionLoad)
            postCommand(_("set auto-solib-add off"));
975
        postCommand(_("set stop-on-solib-events 0"));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
976
    }
977
978
    #endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
979
980
    // nicer to see a bit of the world we live in
    reloadModules();
981
    attemptBreakpointSynchronization();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
982
983
}

con's avatar
con committed
984
985
void GdbEngine::handleAsyncOutput(const GdbMi &data)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
986
    const QByteArray &reason = data.findChild("reason").data();
con's avatar
con committed
987

988
989
    if (isExitedReason(reason)) {
        qq->notifyInferiorExited();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
990
        QString msg;
991
        if (reason == "exited") {
992
993
994
995
996
            msg = tr("Program exited with exit code %1")
                .arg(_(data.findChild("exit-code").toString()));
        } else if (reason == "exited-signalled" || reason == "signal-received") {
            msg = tr("Program exited after receiving signal %1")
                .arg(_(data.findChild("signal-name").toString()));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
997
        } else {
998
            msg = tr("Program exited normally");
999
1000
        }
        q->showStatusMessage(msg);
1001
        postCommand(_("-gdb-exit"), CB(handleExit));
1002
1003
1004
1005
        return;
    }


1006
1007
1008
    //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
    //!MAC: startSymbolName == data.findChild("frame").findChild("func")
    if (m_waitingForFirstBreakpointToBeHit) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1009
1010
        m_waitingForFirstBreakpointToBeHit = false;

1011
        // If the executable dies already that early we might get something
1012
        // like >49*stopped,reason="exited",exit-code="0177"
1013
1014
        // This is handled now above.

1015
        qq->notifyInferiorStopped();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1016
        handleAqcuiredInferior();
1017
        m_autoContinue = true;
1018
        return;
1019
1020
    }

hjk's avatar