gdbengine.cpp 144 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
8
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
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
26
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
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
39
40
41
42
43
44
45
46
47
#include "debuggerconstants.h"
#include "debuggermanager.h"
#include "gdbmi.h"
#include "procinterrupt.h"

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

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

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

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

#include <QtGui/QAction>
64
#include <QtGui/QApplication>
con's avatar
con committed
65
66
67
68
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QToolTip>
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
75
76
77
78

#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
#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
static int &currentToken()
{
    static int token = 0;
    return token;
}

106
static const QString tooltipIName = _("tooltip");
107

con's avatar
con committed
108
109
110
111
112
113
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

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

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

137
void GdbEngine::initializeConnections()
con's avatar
con committed
138
139
{
    // Gdb Process interaction
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    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)));
158
159
160
161
    connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readUploadStandardOutput()));
    connect(&m_uploadProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readUploadStandardError()));
162

con's avatar
con committed
163
    // Output
164
    connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
165
        this, SLOT(readDebugeeOutput(QByteArray)));
con's avatar
con committed
166
167
168
169
170
171
172

    connect(this, SIGNAL(gdbOutputAvailable(QString,QString)),
        q, SLOT(showDebuggerOutput(QString,QString)),
        Qt::QueuedConnection);
    connect(this, SIGNAL(gdbInputAvailable(QString,QString)),
        q, SLOT(showDebuggerInput(QString,QString)),
        Qt::QueuedConnection);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
173
174
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        q, SLOT(showApplicationOutput(QString)),
con's avatar
con committed
175
        Qt::QueuedConnection);
176

177
    // FIXME: These trigger even if the engine is not active
178
179
180
181
182
183
    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
184
185
}

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

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
200
    m_autoContinue = false;
201
    m_waitingForFirstBreakpointToBeHit = false;
202
    m_commandsToRunOnTemporaryBreak.clear();
203
204
}

con's avatar
con committed
205
206
207
208
209
void GdbEngine::gdbProcError(QProcess::ProcessError error)
{
    QString msg;
    switch (error) {
        case QProcess::FailedToStart:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
210
            msg = tr("The Gdb process failed to start. Either the "
con's avatar
con committed
211
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
212
                "permissions to invoke the program.")
hjk's avatar
hjk committed
213
                .arg(theDebuggerStringSetting(GdbLocation));
con's avatar
con committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
            break;
        case QProcess::Crashed:
            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().");
    }

238
    q->showStatusMessage(msg);
con's avatar
con committed
239
240
241
242
243
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
    // act as if it was closed by the core
    q->exitDebugger();
}

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
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);
}

281
282
283
284
285
286
287
288
289
290
291
292
void GdbEngine::readUploadStandardOutput()
{
    QByteArray ba = m_uploadProc.readAllStandardOutput();
    gdbOutputAvailable(_("upload-out:"), QString::fromLocal8Bit(ba, ba.length()));
}

void GdbEngine::readUploadStandardError()
{
    QByteArray ba = m_uploadProc.readAllStandardError();
    gdbOutputAvailable(_("upload-err:"), QString::fromLocal8Bit(ba, ba.length()));
}

con's avatar
con committed
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#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

309
310
311
312
313
314
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
    emit applicationOutputAvailable(m_outputCodec->toUnicode(
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
315
316
void GdbEngine::debugMessage(const QString &msg)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
317
    emit gdbOutputAvailable(_("debug:"), msg);
hjk's avatar
hjk committed
318
319
}

320
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
321
322
323
{
    static QTime lastTime;

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
324
    emit gdbOutputAvailable(_("            "), currentTime());
325
    emit gdbOutputAvailable(_("stdout:"), QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
326
327
328
329
330

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
331
332
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
333
        << "size:" << buff.size();
con's avatar
con committed
334
#else
335
    //qDebug() << "buf:" << buff;
con's avatar
con committed
336
337
338
339
#endif

    lastTime = QTime::currentTime();

340
341
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
342

343
344
345
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
346

347
348
349
350
    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
351
            break;
352
353
354
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
355
        //qDebug() << "found token" << token;
356
    }
con's avatar
con committed
357

358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
    // 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
373

374
375
376
            GdbMi record;
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
377
                if (*from != ',') {
hjk's avatar
hjk committed
378
379
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
380
                    record.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
381
382
383
384
385
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
386
                    //qDebug() << "parsed response:" << data.toString();
hjk's avatar
hjk committed
387
388
                    record.m_children += data;
                    record.m_type = GdbMi::Tuple;
con's avatar
con committed
389
390
                }
            }
391
392
393
394
            if (asyncClass == "stopped") {
                handleAsyncOutput(record);
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
395
396
397
398
            } 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",
399
                // symbols-loaded="0"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
400
                QByteArray id = record.findChild("id").data();
401
                if (!id.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
402
                    q->showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
hjk's avatar
hjk committed
403
404
405
406
            } 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
407
408
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
409
410
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
411
412
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
413
414
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
415
416
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 created.").arg(_(id)));
417
418
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
419
420
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
421
422
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
423
424
                QByteArray id = record.findChild("id").data();
                QByteArray groupid = record.findChild("group-id").data();
425
                q->showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
426
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
427
            } else if (asyncClass == "thread-selected") {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
428
429
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
430
                //"{id="2"}" 
431
            #if defined(Q_OS_MAC)
432
433
434
435
436
437
438
439
440
441
442
            } 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 {
443
                qDebug() << "IGNORED ASYNC OUTPUT"
444
                    << asyncClass << record.toString();
445
            }
446
447
            break;
        }
448

449
        case '~': {
450
451
452
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
            if (data.startsWith("Reading symbols from ")) {
453
                q->showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))));
454
            }
455
456
            break;
        }
457

458
        case '@': {
459
460
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingTargetStreamOutput += data;
461
            break;
con's avatar
con committed
462
463
        }

464
465
466
467
468
469
        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
470
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
471
472
473
            break;
        }

474
475
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
476

477
            record.token = token;
con's avatar
con committed
478

479
480
481
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
482

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
483
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
484
485
486
487
488
489
490
491
492
493
494
495
            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
496

497
498
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
499
500
501
502
503
504
505
506
507
                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
508
509
                }
            }
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534

            //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
535
536
537
538
        }
    }
}

539
void GdbEngine::handleStubAttached(const GdbResultRecord &, const QVariant &)
540
541
542
{
    qq->notifyInferiorStopped();
    handleAqcuiredInferior();
543
    m_autoContinue = true;
544
545
546
547
}

void GdbEngine::stubStarted()
{
548
549
550
    const qint64 attachedPID = m_stubProc.applicationPID();
    qq->notifyInferiorPidChanged(attachedPID);
    postCommand(_("attach %1").arg(attachedPID), CB(handleStubAttached));
551
552
553
554
555
556
557
}

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

con's avatar
con committed
558
559
void GdbEngine::readGdbStandardError()
{
560
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
561
562
563
564
}

void GdbEngine::readGdbStandardOutput()
{
565
566
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
567

568
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
569

570
571
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
572
        int end = m_inbuffer.indexOf('\n', scan);
573
574
575
576
577
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
578
        scan = newstart;
579
580
        if (end == start)
            continue;
581
        #if defined(Q_OS_WIN)
582
583
584
585
586
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
587
        #endif
588
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
589
    }
590
    m_inbuffer.clear();
con's avatar
con committed
591
592
593
594
}

void GdbEngine::interruptInferior()
{
595
    qq->notifyInferiorStopRequested();
596

597
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
598
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
599
        qq->notifyInferiorExited();
con's avatar
con committed
600
        return;
601
    }
con's avatar
con committed
602

603
    if (q->startMode() == StartRemote) {
604
        postCommand(_("-exec-interrupt"));
605
606
607
        return;
    }

608
609
    const qint64 attachedPID = q->inferiorPid();
    if (attachedPID <= 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
610
        debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
con's avatar
con committed
611
612
613
        return;
    }

614
615
    if (!interruptProcess(attachedPID))
        debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
con's avatar
con committed
616
617
618
619
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
620
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
621
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
622
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
623
624
        return;
    }
625
    if (pid == q->inferiorPid())
con's avatar
con committed
626
        return;
627
628
    debugMessage(_("FOUND PID %1").arg(pid));    

Roberto Raggi's avatar
Roberto Raggi committed
629
    qq->notifyInferiorPidChanged(pid);
630
631
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
632
633
}

634
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
635
636
                            const char *callbackName, const QVariant &cookie)
{
637
    postCommand(command, NoFlags, callback, callbackName, cookie);
638
639
}

640
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
641
642
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
con's avatar
con committed
643
644
{
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
645
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + command);
con's avatar
con committed
646
647
648
        return;
    }

649
    if (flags & RebuildModel) {
con's avatar
con committed
650
        ++m_pendingRequests;
651
        PENDING_DEBUG("   CALLBACK" << callbackName << "INCREMENTS PENDING TO:"
con's avatar
con committed
652
653
            << m_pendingRequests << command);
    } else {
654
        PENDING_DEBUG("   UNKNOWN CALLBACK" << callbackName << "LEAVES PENDING AT:"
con's avatar
con committed
655
656
657
            << m_pendingRequests << command);
    }

658
    GdbCommand cmd;
con's avatar
con committed
659
    cmd.command = command;
660
661
662
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
con's avatar
con committed
663
664
    cmd.cookie = cookie;

665
    if ((flags & NeedsStop) && q->status() != DebuggerInferiorStopped
666
667
668
            && q->status() != DebuggerProcessStartingUp) {
        // queue the commands that we cannot send at once
        QTC_ASSERT(q->status() == DebuggerInferiorRunning,
669
            qDebug() << "STATUS:" << q->status());
670
        q->showStatusMessage(tr("Stopping temporarily."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
671
        debugMessage(_("QUEUING COMMAND ") + cmd.command);
672
673
674
        m_commandsToRunOnTemporaryBreak.append(cmd);
        interruptInferior();
    } else if (!command.isEmpty()) {
675
        flushCommand(cmd);
con's avatar
con committed
676
677
678
    }
}

679
680
681
682
683
void GdbEngine::flushCommand(GdbCommand &cmd)
{
    ++currentToken();
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
684
    if (cmd.flags & EmbedToken)
685
686
687
688
689
690
691
692
        cmd.command = cmd.command.arg(currentToken());

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

con's avatar
con committed
693
694
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
695
696
    //qDebug() << "TOKEN:" << record.token
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
con's avatar
con committed
697
698
699
700
701
702
703
    //qDebug() << "";
    //qDebug() << "\nRESULT" << record.token << record.toString();

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

704
    GdbCommand cmd = m_cookieForToken.take(token);
con's avatar
con committed
705

706
    if (record.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
707
        //qDebug() << "### SKIPPING OLD RESULT" << record.toString();
con's avatar
con committed
708
709
710
711
712
        //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
        return;
    }

#if 0
713
714
715
    qDebug() << "# handleOutput,"
        << "cmd type:" << cmd.type
        << " cmd synchronized:" << cmd.synchronized
con's avatar
con committed
716
717
718
719
720
        << "\n record: " << record.toString();
#endif

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

721
722
    if (cmd.callback)
        (this->*(cmd.callback))(record, cmd.cookie);
con's avatar
con committed
723

724
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
725
726
727
        --m_pendingRequests;
        PENDING_DEBUG("   TYPE " << cmd.type << " DECREMENTS PENDING TO: "
            << m_pendingRequests << cmd.command);
728
729
        if (m_pendingRequests <= 0) {
            PENDING_DEBUG(" ....  AND TRIGGERS MODEL UPDATE");
con's avatar
con committed
730
            updateWatchModel2();
731
        }
con's avatar
con committed
732
733
734
735
    } else {
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
            << m_pendingRequests << cmd.command);
    }
736
737
738
739
740
741
742
743
744
745

    // 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
746
747
748
749
750
}

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

755
    m_gdbProc.write(command.toLocal8Bit() + "\r\n");
con's avatar
con committed
756
757
}

758
void GdbEngine::handleTargetCore(const GdbResultRecord &, const QVariant &)
759
760
{
    qq->notifyInferiorStopped();
761
762
    q->showStatusMessage(tr("Core file loaded."));
    q->resetLocation();
763
    tryLoadDebuggingHelpers();
764
765
    qq->stackHandler()->setCurrentIndex(0);
    updateLocals(); // Quick shot
766
    reloadStack();
767
    if (supportsThreads())
768
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
769
    qq->reloadRegisters();
770
771
}

772
void GdbEngine::handleQuerySources(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
773
774
{
    if (record.resultClass == GdbResultDone) {
775
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
776
777
778
779
780
781
        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
782
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
783
            GdbMi fullName = item.findChild("fullname");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
784
            QString full = QString::fromLocal8Bit(fullName.data());
con's avatar
con committed
785
786
787
788
            #ifdef Q_OS_WIN
            full = QDir::cleanPath(full);
            #endif
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
789
                //qDebug() << "STORING 2:" << fileName << full;
con's avatar
con committed
790
791
792
793
                m_shortToFullName[fileName] = full;
                m_fullToShortName[full] = fileName;
            }
        }
794
795
        if (m_shortToFullName != oldShortToFull)
            qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
con's avatar
con committed
796
797
798
    }
}

799
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
800
{
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
    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;
        }
818
    }
819
820
821
822
823
    // check "* 3 Thread ..."
    QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
    Q_ASSERT(re.isValid());
    if (re.indexIn(data) != -1)
        maybeHandleInferiorPidChanged(re.cap(1));
824
825
}

826
void GdbEngine::handleInfoProc(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
827
828
829
830
{
    if (record.resultClass == GdbResultDone) {
        #if defined(Q_OS_MAC)
        //^done,process-id="85075"
Oswald Buddenhagen's avatar
nicer    
Oswald Buddenhagen committed
831
        maybeHandleInferiorPidChanged(_(record.data.findChild("process-id").data()));
con's avatar
con committed
832
833
834
835
        #endif

        #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
        // FIXME: use something more robust
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
836
837
        QRegExp re(__("process (\\d+)"));
        QString data = __(record.data.findChild("consolestreamoutput").data());
con's avatar
con committed
838
839
840
841
842
843
        if (re.indexIn(data) != -1)
            maybeHandleInferiorPidChanged(re.cap(1));
        #endif
    }
}

844
void GdbEngine::handleInfoShared(const GdbResultRecord &record, const QVariant &cookie)
con's avatar
con committed
845
846
847
{
    if (record.resultClass == GdbResultDone) {
        // let the modules handler do the parsing
848
        handleModulesList(record, cookie);
con's avatar
con committed
849
850
851
    }
}

852
#if 0
con's avatar
con committed
853
854
855
856
857
858
859
860
861
862
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();
863
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
864
    QByteArray output = record.data.findChild("logstreamoutput").data();
865
    if (output.isEmpty())
con's avatar
con committed
866
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
867
868
869
870
871
872
873
874
875
    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
876
}
877
#endif
con's avatar
con committed
878

879
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
880
881
882
883
884
885
886
887
{
    // 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();
888
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
con's avatar
con committed
889
    GdbMi frame = record.data.findChild("frame");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
890
    QString file = QString::fromLocal8Bit(frame.findChild("fullname").data());
con's avatar
con committed
891
    int line = frame.findChild("line").data().toInt();
892
893
    qDebug() << "HIT:" << file << line << "IN" << frame.toString()
        << "--" << record.toString();
con's avatar
con committed
894
895
896
    q->gotoLocation(file, line, true);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
897
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
898
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
899
900
901
902
    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
903
904
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
905
static bool isStoppedReason(const QByteArray &reason)
con's avatar
con committed
906
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
907
908
909
910
911
912
913
    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"
914
        #ifdef Q_OS_MAC
con's avatar
con committed
915
        || reason.isEmpty()
916
        #endif
con's avatar
con committed
917
918
919
    ;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
920
921
922
void GdbEngine::handleAqcuiredInferior()
{
    #if defined(Q_OS_WIN)
923
    postCommand(_("info thread"), CB(handleInfoThreads));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
924
925
    #endif
    #if defined(Q_OS_LINUX)
926
    postCommand(_("info proc"), CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
927
928
    #endif
    #if defined(Q_OS_MAC)
929
    postCommand(_("info pid"), NeedsStop, CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
930
    #endif
931
    if (theDebuggerBoolSetting(ListSourceFiles))
932
        reloadSourceFiles();
933

934
935
936
937
    // Reverse debugging. FIXME: Should only be used when available.
    if (theDebuggerBoolSetting(EnableReverseDebugging))
        postCommand(_("target record"));

938
    tryLoadDebuggingHelpers();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
939

940
    #ifndef Q_OS_MAC
941
    // intentionally after tryLoadDebuggingHelpers(),
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
942
    // otherwise we'd interupt solib loading.
hjk's avatar
hjk committed
943
    if (theDebuggerBoolSetting(AllPluginBreakpoints)) {
944
945
946
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 0"));
        postCommand(_("sharedlibrary .*"));
hjk's avatar
hjk committed
947
    } else if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
948
949
950
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 1"));
        postCommand(_("sharedlibrary ")
hjk's avatar
hjk committed
951
952
          + theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
    } else if (theDebuggerBoolSetting(NoPluginBreakpoints)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
953
        // should be like that already
954
955
        if (!m_dumperInjectionLoad)
            postCommand(_("set auto-solib-add off"));
956
        postCommand(_("set stop-on-solib-events 0"));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
957
    }
958
959
    #endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
960
961
    // nicer to see a bit of the world we live in
    reloadModules();
962
    attemptBreakpointSynchronization();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
963
964
}

con's avatar
con committed
965
966
void GdbEngine::handleAsyncOutput(const GdbMi &data)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
967
    const QByteArray &reason = data.findChild("reason").data();
con's avatar
con committed
968

969
970
    if (isExitedReason(reason)) {
        qq->notifyInferiorExited();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
971
        QString msg;
972
        if (reason == "exited") {
973
974
975
976
977
            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
978
        } else {
979
            msg = tr("Program exited normally");
980
981
        }
        q->showStatusMessage(msg);
982
        postCommand(_("-gdb-exit"), CB(handleExit));
983
984
985
986
        return;
    }


987
988
989
    //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
990
991
        m_waitingForFirstBreakpointToBeHit = false;

992
993
994
995
        // If the executable dies already that early we might get something
        // like stdout:49*stopped,reason="exited",exit-code="0177"
        // This is handled now above.

996
        qq->notifyInferiorStopped();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
997
        handleAqcuiredInferior();
998
        m_autoContinue = true;
999
        return;
1000
1001
    }

1002
    if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
Roberto Raggi's avatar
Roberto Raggi committed
1003
        QTC_ASSERT(q->status() == DebuggerInferiorStopRequested,
1004
            qDebug() << "STATUS:" << q->status())
1005
1006
        qq->notifyInferiorStopped();
        // FIXME: racy
1007
1008
        while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
            GdbCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1009
            debugMessage(_("RUNNING QUEUED COMMAND %1 %2")
1010
1011
                .arg(cmd.command).arg(_(cmd.callbackName)));
            flushCommand(cmd);
1012
        }
1013
1014
        q->showStatusMessage(tr("Processing queued commands."));
        m_autoContinue = true;
1015
1016
1017
        return;
    }

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1018
    const QByteArray &msg = data.findChild("consolestreamoutput").data();
1019
    if (msg.contains("Stopped due to shared library event") || reason.isEmpty()) {
hjk's avatar
hjk committed
1020
        if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1021
1022
            QString dataStr = _(data.toString());
            debugMessage(_("SHARED LIBRARY EVENT: ") + dataStr);
hjk's avatar
hjk committed
1023
            QString pat = theDebuggerStringSetting(SelectedPluginBreakpointsPattern);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1024
            debugMessage(_("PATTERN: ") + pat);
1025
            postCommand(_("sharedlibrary ") + pat);
con's avatar
con committed
1026
            continueInferior();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1027
            q->showStatusMessage(tr("Loading %1...").arg(dataStr));
1028
            return;
con's avatar
con committed
1029
        }
1030
        m_modulesListOutdated = true;
1031
        // fall through
con's avatar
con committed
1032
1033
    }

1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
    // seen on XP after removing a breakpoint while running
    //  stdout:945*stopped,reason="signal-received",signal-name="SIGTRAP",
    //  signal-meaning="Trace/breakpoint trap",thread-id="2",
    //  frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
    //  args=[],from="C:\\WINDOWS\\system32\\ntdll.dll"}
    if (reason == "signal-received"
          && data.findChild("signal-name").toString() == "SIGTRAP") {
        continueInferior();
        return;
    }

con's avatar
con committed
1045
1046
    // jump over well-known frames
    static int stepCounter = 0;
hjk's avatar
hjk committed
1047
    if (theDebuggerBoolSetting(SkipKnownFrames)) {
con's avatar
con committed
1048
1049
        if (reason == "end-stepping-range" || reason == "function-finished") {
            GdbMi frame = data.findChild("frame");
hjk's avatar
hjk committed
1050
            //debugMessage(frame.toString());
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1051
1052
            m_currentFrame = _(frame.findChild("addr").data() + '%' +
                 frame.findChild("func").data() + '%');
con's avatar