gdbengine.cpp 158 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
con's avatar
con committed
29

30
31
#define QT_NO_CAST_FROM_ASCII

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

hjk's avatar
hjk committed
163
static QByteArray parsePlainConsoleStream(const GdbResponse &response)
164
{
hjk's avatar
hjk committed
165
    GdbMi output = response.data.findChild("consolestreamoutput");
166
167
168
169
170
171
172
173
174
175
    QByteArray out = output.data();
    // FIXME: proper decoding needed
    if (out.endsWith("\\n"))
        out.chop(2);
    while (out.endsWith('\n') || out.endsWith(' '))
        out.chop(1);
    int pos = out.indexOf(" = ");
    return out.mid(pos + 3);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    m_inbuffer.clear();

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

285
286
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
287
    m_dumperHelper.clear();
288
289
290
#ifdef Q_OS_LINUX
    m_entryPoint.clear();
#endif
291
292
}

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

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

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

hjk's avatar
hjk committed
342
343
void GdbEngine::debugMessage(const QString &msg)
{
344
    gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
345
346
}

347
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
348
349
350
{
    static QTime lastTime;

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

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

    lastTime = QTime::currentTime();

368
369
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
370

371
372
373
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
374

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

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

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

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

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

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

518
        case '@': {
519
            readDebugeeOutput(GdbMi::parseCString(from, to));
520
            break;
con's avatar
con committed
521
522
        }

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

533
        case '^': {
hjk's avatar
hjk committed
534
            GdbResponse response;
con's avatar
con committed
535

hjk's avatar
hjk committed
536
            response.token = token;
con's avatar
con committed
537

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

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

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

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

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

void GdbEngine::readGdbStandardError()
{
599
600
601
602
    QByteArray err = m_gdbProc.readAllStandardError();
    if (err == "Undefined command: \"bb\".  Try \"help\".\n")
        return;
    qWarning() << "Unexpected gdb stderr:" << err;
con's avatar
con committed
603
604
605
606
}

void GdbEngine::readGdbStandardOutput()
{
607
608
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
609

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
610
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
611

612
613
614
615
    // This can trigger when a dialog starts a nested event loop
    if (m_busy)
        return;

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

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

hjk's avatar
hjk committed
645
646
647
    setState(InferiorStopping);
    showStatusMessage(tr("Stop requested..."), 5000);

648
    debugMessage(_("TRYING TO INTERRUPT INFERIOR"));
hjk's avatar
hjk committed
649
    m_gdbAdapter->interruptInferior();
con's avatar
con committed
650
651
652
653
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
654
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
655
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
656
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
657
658
        return;
    }
hjk's avatar
hjk committed
659
    if (pid == inferiorPid())
con's avatar
con committed
660
        return;
661
662
    debugMessage(_("FOUND PID %1").arg(pid));    

663
    handleInferiorPidChanged(pid);
664
665
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
666
667
}

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

687
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
688
689
                            const char *callbackName, const QVariant &cookie)
{
690
    postCommand(command, NoFlags, callback, callbackName, cookie);
691
692
}

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

hjk's avatar
hjk committed
715
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
716
        ++m_pendingRequests;
717
718
        PENDING_DEBUG("   MODEL:" << cmd.command << "=>" << cmd.callbackName
                      << "INCREMENTS PENDING TO" << m_pendingRequests);
con's avatar
con committed
719
    } else {
720
721
        PENDING_DEBUG("   OTHER (IN):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
722
723
    }

724
    if ((cmd.flags & NeedsStop) || !m_commandsToRunOnTemporaryBreak.isEmpty()) {
725
726
        if (state() == InferiorStopped
            || state() == InferiorStarting || state() == AdapterStarted) {
hjk's avatar
hjk committed
727
728
729
730
731
732
            // 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);
733
734
735
736
737
            if (state() == InferiorStopping) {
                debugMessage(_("CHILD ALREADY BEING INTERRUPTED"));
            } else if (state() == InferiorRunningRequested) {
                debugMessage(_("RUNNING REQUESTED; POSTPONING INTERRUPT"));
            } else if (state() == InferiorRunning) {
738
                showStatusMessage(tr("Stopping temporarily."), 1000);
739
740
741
                interruptInferior();
            } else {
                qDebug() << "ATTEMPTING TO QUEUE COMMAND IN INAPPROPRIATE STATE" << state();
742
            }
hjk's avatar
hjk committed
743
        }
hjk's avatar
hjk committed
744
    } else if (!cmd.command.isEmpty()) {
745
        flushCommand(cmd);
con's avatar
con committed
746
747
748
    }
}

hjk's avatar
hjk committed
749
void GdbEngine::flushCommand(const GdbCommand &cmd0)
750
{
hjk's avatar
hjk committed
751
    GdbCommand cmd = cmd0;
hjk's avatar
hjk committed
752
    if (state() == DebuggerNotReady) {
753
        gdbInputAvailable(LogInput, cmd.command);
754
755
756
757
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + cmd.command);
        return;
    }

758
    ++currentToken();
759
    cmd.postTime = QTime::currentTime();
760
761
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
762
    if (cmd.flags & EmbedToken)
763
        cmd.command = cmd.command.arg(currentToken());
764
    gdbInputAvailable(LogInput, cmd.command);
765
766

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

769
void GdbEngine::handleResultRecord(GdbResponse *response)
con's avatar
con committed
770
{
hjk's avatar
hjk committed
771
    //qDebug() << "TOKEN:" << response.token
772
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
hjk's avatar
hjk committed
773
    //qDebug() << "\nRESULT" << response.token << response.toString();
con's avatar
con committed
774

775
    int token = response->token;
con's avatar
con committed
776
777
778
    if (token == -1)
        return;

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

834
    GdbCommand cmd = m_cookieForToken.take(token);
835
    if (theDebuggerBoolSetting(LogTimeStamps)) {
836
        gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
837
838
839
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
840

841
    if (response->token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
842
        //debugMessage(_("### SKIPPING OLD RESULT") + response.toString());
con's avatar
con committed
843
844
845
        return;
    }

846
    response->cookie = cmd.cookie;
hjk's avatar
hjk committed
847

848
849
850
851
852
    if (response->resultClass != GdbResultError &&
        response->resultClass != ((cmd.flags & RunRequest) ? GdbResultRunning :
                                  (cmd.flags & ExitRequest) ? GdbResultExit :
                                  GdbResultDone)) {
        QString rsp = _(GdbResponse::stringFromResultClass(response->resultClass));
853
854
        qWarning() << "UNEXPECTED RESPONSE " << rsp << " TO COMMAND" << cmd.command << " AT " __FILE__ ":" STRINGIFY(__LINE__);
        debugMessage(_("UNEXPECTED RESPONSE %1 TO COMMAND %2").arg(rsp).arg(cmd.command));
855
856
    } else {
        if (cmd.callback)
857
            (this->*cmd.callback)(*response);
858
        else if (cmd.adapterCallback)
859
            (m_gdbAdapter->*cmd.adapterCallback)(*response);
860
    }
con's avatar
con committed
861

862
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
863
        --m_pendingRequests;
864
865
        PENDING_DEBUG("   WATCH" << cmd.command << "=>" << cmd.callbackName
                      << "DECREMENTS PENDING TO" << m_pendingRequests);
866
        if (m_pendingRequests <= 0) {
867
            PENDING_DEBUG("\n\n ... AND TRIGGERS MODEL UPDATE\n");
hjk's avatar
hjk committed
868
            rebuildModel();
869
        }
con's avatar
con committed
870
    } else {
871
872
        PENDING_DEBUG("   OTHER (OUT):" << cmd.command << "=>" << cmd.callbackName
                      << "LEAVES PENDING AT" << m_pendingRequests);
con's avatar
con committed
873
    }
874

875
876
877
878
879
880
881
    // Commands were queued, but we were in RunningRequested state, so the interrupt
    // was postponed.
    // This is done after the command callbacks so the running-requesting commands
    // can assert on the right state.
    if (state() == InferiorRunning && !m_commandsToRunOnTemporaryBreak.isEmpty())
        interruptInferior();

882
883
    // Continue only if there are no commands wire anymore, so this will
    // be fully synchroneous.
884
885
886
887
    // 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.
888
    if (m_commandsDoneCallback && m_cookieForToken.isEmpty()) {
889
        debugMessage(_("ALL COMMANDS DONE; INVOKING CALLBACK"));
890
891
        CommandsDoneCallback cont = m_commandsDoneCallback;
        m_commandsDoneCallback = 0;
hjk's avatar
hjk committed
892
893
894
        (this->*cont)();
    } else {
        PENDING_DEBUG("MISSING TOKENS: " << m_cookieForToken.keys());
895
    }
con's avatar
con committed
896
897
898
899
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
hjk's avatar
hjk committed
900
    if (state() == DebuggerNotReady) {
901
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
902
903
904
        return;
    }

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