gdbengine.cpp 143 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
    initializeVariables();
    initializeConnections();
con's avatar
con committed
126
127
128
129
}

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

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

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

    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
170
171
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        q, SLOT(showApplicationOutput(QString)),
con's avatar
con committed
172
        Qt::QueuedConnection);
173

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

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

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

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

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

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

241
242
243
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
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);
}

278
279
280
281
282
283
284
285
286
287
288
289
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#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

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

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

317
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
318
319
320
{
    static QTime lastTime;

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

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

    lastTime = QTime::currentTime();

337
338
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
339

340
341
342
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
343

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

355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    // 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
370

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

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

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

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

471
472
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
473

474
            record.token = token;
con's avatar
con committed
475

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

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

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

            //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
532
533
534
535
        }
    }
}

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

void GdbEngine::stubStarted()
{
    q->m_attachedPID = m_stubProc.applicationPID();
    qq->notifyInferiorPidChanged(q->m_attachedPID);
547
    postCommand(_("attach %1").arg(q->m_attachedPID), CB(handleStubAttached));
548
549
550
551
552
553
554
}

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

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

void GdbEngine::readGdbStandardOutput()
{
562
563
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
564

565
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
566

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

void GdbEngine::interruptInferior()
{
592
    qq->notifyInferiorStopRequested();
593

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

600
    if (q->startMode() == StartRemote) {
601
        postCommand(_("-exec-interrupt"));
602
603
604
        return;
    }

605
    if (q->m_attachedPID <= 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
606
        debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
con's avatar
con committed
607
608
609
        return;
    }

610
    if (!interruptProcess(q->m_attachedPID))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
611
        debugMessage(_("CANNOT INTERRUPT %1").arg(q->m_attachedPID));
con's avatar
con committed
612
613
614
615
616
617
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
    int pid = pid0.toInt();
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
618
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
619
620
        return;
    }
hjk's avatar
hjk committed
621
    if (pid == q->m_attachedPID)
con's avatar
con committed
622
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
623
    debugMessage(_("FOUND PID %1").arg(pid));
hjk's avatar
hjk committed
624
    q->m_attachedPID = pid;
Roberto Raggi's avatar
Roberto Raggi committed
625
    qq->notifyInferiorPidChanged(pid);
626
627
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
628
629
}

630
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
631
632
                            const char *callbackName, const QVariant &cookie)
{
633
    postCommand(command, NoFlags, callback, callbackName, cookie);
634
635
}

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

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

654
    GdbCommand cmd;
con's avatar
con committed
655
    cmd.command = command;
656
657
658
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
con's avatar
con committed
659
660
    cmd.cookie = cookie;

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

675
676
677
678
679
void GdbEngine::flushCommand(GdbCommand &cmd)
{
    ++currentToken();
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
680
    if (cmd.flags & EmbedToken)
681
682
683
684
685
686
687
688
        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
689
690
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
691
692
    //qDebug() << "TOKEN:" << record.token
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
con's avatar
con committed
693
694
695
696
697
698
699
    //qDebug() << "";
    //qDebug() << "\nRESULT" << record.token << record.toString();

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

700
    GdbCommand cmd = m_cookieForToken.take(token);
con's avatar
con committed
701

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

#if 0
709
710
711
    qDebug() << "# handleOutput,"
        << "cmd type:" << cmd.type
        << " cmd synchronized:" << cmd.synchronized
con's avatar
con committed
712
713
714
715
716
        << "\n record: " << record.toString();
#endif

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

717
718
    if (cmd.callback)
        (this->*(cmd.callback))(record, cmd.cookie);
con's avatar
con committed
719

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

    // 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
742
743
744
745
746
}

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

751
    m_gdbProc.write(command.toLocal8Bit() + "\r\n");
con's avatar
con committed
752
753
}

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

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

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

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

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

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

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

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
893
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
894
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
895
896
897
898
    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
899
900
}

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

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

930
    tryLoadDebuggingHelpers();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
931

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
952
953
    // nicer to see a bit of the world we live in
    reloadModules();
954
    attemptBreakpointSynchronization();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
955
956
}

con's avatar
con committed
957
958
void GdbEngine::handleAsyncOutput(const GdbMi &data)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
959
    const QByteArray &reason = data.findChild("reason").data();
con's avatar
con committed
960

961
962
    if (isExitedReason(reason)) {
        qq->notifyInferiorExited();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
963
        QString msg;
964
        if (reason == "exited") {
965
966
967
968
969
            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
970
        } else {
971
            msg = tr("Program exited normally");
972
973
        }
        q->showStatusMessage(msg);
974
        postCommand(_("-gdb-exit"), CB(handleExit));
975
976
977
978
        return;
    }


979
980
981
    //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
982
983
        m_waitingForFirstBreakpointToBeHit = false;

984
985
986
987
        // 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.

988
        qq->notifyInferiorStopped();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
989
        handleAqcuiredInferior();
990
        m_autoContinue = true;
991
        return;
992
993
    }

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

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

1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
    // 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
1037
1038
    // jump over well-known frames
    static int stepCounter = 0;
hjk's avatar
hjk committed
1039
    if (theDebuggerBoolSetting(SkipKnownFrames)) {
con's avatar
con committed
1040
1041
        if (reason == "end-stepping-range" || reason == "function-finished") {
            GdbMi frame = data.findChild("frame");
hjk's avatar
hjk committed
1042
            //debugMessage(frame.toString());
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1043
1044
            m_currentFrame = _(frame.findChild("addr").data() + '%' +
                 frame.findChild("func").data() + '%');
con's avatar
con committed
1045

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1046
1047
            QString funcName = _(frame.findChild("func").data());
            QString fileName = QString::fromLocal8Bit(frame.findChild("file").data());
con's avatar
con committed
1048
            if (isLeavableFunction(funcName, fileName)) {
Oswald Buddenhagen's avatar