gdbengine.cpp 137 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 <coreplugin/icore.h>
hjk's avatar
hjk committed
54

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

#include <QtGui/QAction>
63
#include <QtGui/QApplication>
con's avatar
con committed
64
65
66
67
#include <QtGui/QLabel>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QToolTip>
68
69
#include <QtGui/QDialogButtonBox>
#include <QtGui/QPushButton>
con's avatar
con committed
70
71
72
73
74

#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
75
#include <ctype.h>
con's avatar
con committed
76
77
78
79
80
81
82
83
84
85
86
87
88

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
89
#   define PENDING_DEBUG(s)
con's avatar
con committed
90
91
#endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
92
93
94
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)

95
96
97
typedef QLatin1Char _c;
typedef QLatin1String __;
static inline QString _(const char *s) { return QString::fromLatin1(s); }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
98
99

static const QString tooltipIName = _("tooltip");
con's avatar
con committed
100
101
102
103
104
105
106

static int &currentToken()
{
    static int token = 0;
    return token;
}

107
108
109
110
111
112
113
114
115
#define execCommand(command,callback) \
    execCommandInternal(command, NoFlags, &GdbEngine::callback, STRINGIFY(callback), QVariant())
#define execCommandF(command,callback,flags) \
    execCommandInternal(command, flags, &GdbEngine::callback, STRINGIFY(callback), QVariant())
#define execCommandC(command,callback,cookie) \
    execCommandInternal(command, NoFlags, &GdbEngine::callback, STRINGIFY(callback), cookie)
#define execCommandFC(command,callback,flags,cookie) \
    execCommandInternal(command, flags, &GdbEngine::callback, STRINGIFY(callback), cookie)

con's avatar
con committed
116
117
118
119
120
121
122
123
124
125
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

GdbEngine::GdbEngine(DebuggerManager *parent)
{
    q = parent;
    qq = parent->engineInterface();
126
    m_stubProc.setDebug(true);
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
140
141
142
143
144
145
146
147
148
{
    // Gdb Process interaction
    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()));

149
150
151
152
    connect(&m_stubProc, SIGNAL(processError(QString)), SLOT(stubError(QString)));
    connect(&m_stubProc, SIGNAL(processStarted()), SLOT(stubStarted()));
    connect(&m_stubProc, SIGNAL(wrapperStopped()), q, SLOT(exitDebugger()));

con's avatar
con committed
153
    // Output
154
155
    connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
            SLOT(readDebugeeOutput(QByteArray)));
con's avatar
con committed
156
157
158
159
160
161
162

    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
163
164
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        q, SLOT(showApplicationOutput(QString)),
con's avatar
con committed
165
        Qt::QueuedConnection);
166

167
168
169
170
171
172
    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()));
173

174
175
    connect(theDebuggerAction(ExpandStack), SIGNAL(triggered()),
        this, SLOT(reloadFullStack()));
hjk's avatar
hjk committed
176
177
    connect(theDebuggerAction(MaximalStackDepth), SIGNAL(triggered()),
        this, SLOT(reloadFullStack()));
con's avatar
con committed
178
179
}

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

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
    m_waitingForBreakpointSynchronizationToContinue = false;
195
    m_waitingForFirstBreakpointToBeHit = false;
196
    m_commandsToRunOnTemporaryBreak.clear();
197
198
}

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

232
    q->showStatusMessage(msg);
con's avatar
con committed
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
    // act as if it was closed by the core
    q->exitDebugger();
}

#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

254
255
256
257
258
259
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
    emit applicationOutputAvailable(m_outputCodec->toUnicode(
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
260
261
void GdbEngine::debugMessage(const QString &msg)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
262
    emit gdbOutputAvailable(_("debug:"), msg);
hjk's avatar
hjk committed
263
264
}

265
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
266
267
268
{
    static QTime lastTime;

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
269
270
    emit gdbOutputAvailable(_("            "), currentTime());
    emit gdbOutputAvailable(_("stdout:"), QString::fromLocal8Bit(buff));
con's avatar
con committed
271
272
273
274
275

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
276
277
278
        << "buf: " << buff.left(1500) << "..."
        //<< "buf: " << buff
        << "size:" << buff.size();
con's avatar
con committed
279
#else
280
    //qDebug() << "buf: " << buff;
con's avatar
con committed
281
282
283
284
#endif

    lastTime = QTime::currentTime();

285
286
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
287

288
289
290
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
291

292
293
294
295
    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
296
            break;
297
298
299
300
301
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
        //qDebug() << "found token " << token;
    }
con's avatar
con committed
302

303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
    // 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
318

319
320
321
            GdbMi record;
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
322
                if (*from != ',') {
hjk's avatar
hjk committed
323
324
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
325
                    record.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
326
327
328
329
330
331
332
333
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
                    //qDebug() << "parsed response: " << data.toString();
                    record.m_children += data;
                    record.m_type = GdbMi::Tuple;
con's avatar
con committed
334
335
                }
            }
336
337
338
339
            if (asyncClass == "stopped") {
                handleAsyncOutput(record);
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
340
341
342
343
            } 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",
344
                // symbols-loaded="0"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
345
                QByteArray id = record.findChild("id").data();
346
                if (!id.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
347
                    q->showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
hjk's avatar
hjk committed
348
349
350
351
            } 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
352
353
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
354
355
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
356
357
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
358
359
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
360
361
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 created.").arg(_(id)));
362
363
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
364
365
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
366
367
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
368
369
                QByteArray id = record.findChild("id").data();
                QByteArray groupid = record.findChild("group-id").data();
370
                q->showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
371
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
372
            } else if (asyncClass == "thread-selected") {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
373
374
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
375
                //"{id="2"}" 
376
377
378
379
380
381
382
383
384
385
386
387
388
389
            #ifdef Q_OS_MAC
            } 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 {
                qDebug() << "IGNORED ASYNC OUTPUT "
                    << asyncClass << record.toString();
390
            }
391
392
            break;
        }
393

394
        case '~': {
395
396
397
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
            if (data.startsWith("Reading symbols from ")) {
398
                q->showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))));
399
            }
400
401
            break;
        }
402

403
        case '@': {
404
405
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingTargetStreamOutput += data;
406
            break;
con's avatar
con committed
407
408
        }

409
410
411
412
413
414
        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
415
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
416
417
418
            break;
        }

419
420
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
421

422
            record.token = token;
con's avatar
con committed
423

424
425
426
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
427

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
428
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
429
430
431
432
433
434
435
436
437
438
439
440
            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
441

442
443
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
444
445
446
447
448
449
450
451
452
                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
453
454
                }
            }
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479

            //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
480
481
482
483
        }
    }
}

484
void GdbEngine::handleStubAttached(const GdbResultRecord &, const QVariant &)
485
486
487
488
489
490
491
492
493
494
{
    qq->notifyInferiorStopped();
    m_waitingForBreakpointSynchronizationToContinue = true;
    handleAqcuiredInferior();
}

void GdbEngine::stubStarted()
{
    q->m_attachedPID = m_stubProc.applicationPID();
    qq->notifyInferiorPidChanged(q->m_attachedPID);
495
    execCommand(_("attach ") + QString::number(q->m_attachedPID), handleStubAttached);
496
497
498
499
500
501
502
}

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

con's avatar
con committed
503
504
void GdbEngine::readGdbStandardError()
{
505
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
506
507
508
509
}

void GdbEngine::readGdbStandardOutput()
{
510
511
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
512

513
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
514

515
516
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
517
        int end = m_inbuffer.indexOf('\n', scan);
518
519
520
521
522
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
523
        scan = newstart;
524
525
        if (end == start)
            continue;
526
        #ifdef Q_OS_WIN
527
528
529
530
531
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
532
        #endif
533
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
534
    }
535
    m_inbuffer.clear();
con's avatar
con committed
536
537
538
539
}

void GdbEngine::interruptInferior()
{
540
541
    qq->notifyInferiorStopRequested();
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
542
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
543
        qq->notifyInferiorExited();
con's avatar
con committed
544
        return;
545
    }
con's avatar
con committed
546

547
    if (q->m_attachedPID <= 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
548
        debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
con's avatar
con committed
549
550
551
        return;
    }

552
    if (!interruptProcess(q->m_attachedPID))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
553
        debugMessage(_("CANNOT INTERRUPT %1").arg(q->m_attachedPID));
con's avatar
con committed
554
555
556
557
558
559
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
    int pid = pid0.toInt();
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
560
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
561
562
        return;
    }
hjk's avatar
hjk committed
563
    if (pid == q->m_attachedPID)
con's avatar
con committed
564
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
565
    debugMessage(_("FOUND PID %1").arg(pid));
hjk's avatar
hjk committed
566
    q->m_attachedPID = pid;
Roberto Raggi's avatar
Roberto Raggi committed
567
    qq->notifyInferiorPidChanged(pid);
con's avatar
con committed
568
569
}

570
571
572
void GdbEngine::execCommandInternal(const QString &command, GdbCommandFlags flags,
                                    GdbCommandCallback callback, const char *callbackName,
                                    const QVariant &cookie)
con's avatar
con committed
573
574
{
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
575
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + command);
con's avatar
con committed
576
577
578
        return;
    }

579
    if (flags & RebuildModel) {
con's avatar
con committed
580
        ++m_pendingRequests;
581
        PENDING_DEBUG("   CALLBACK " << callbackName << " INCREMENTS PENDING TO: "
con's avatar
con committed
582
583
            << m_pendingRequests << command);
    } else {
584
        PENDING_DEBUG("   UNKNOWN CALLBACK " << callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
585
586
587
            << m_pendingRequests << command);
    }

588
    GdbCommand cmd;
con's avatar
con committed
589
    cmd.command = command;
590
591
592
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
con's avatar
con committed
593
594
    cmd.cookie = cookie;

595
    if ((flags & NeedsStop) && q->status() != DebuggerInferiorStopped
596
597
598
599
600
            && q->status() != DebuggerProcessStartingUp) {
        // queue the commands that we cannot send at once
        QTC_ASSERT(q->status() == DebuggerInferiorRunning,
            qDebug() << "STATUS: " << q->status());
        q->showStatusMessage(tr("Stopping temporarily."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
601
        debugMessage(_("QUEUING COMMAND ") + cmd.command);
602
603
604
        m_commandsToRunOnTemporaryBreak.append(cmd);
        interruptInferior();
    } else if (!command.isEmpty()) {
605
        flushCommand(cmd);
con's avatar
con committed
606
607
608
    }
}

609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
void GdbEngine::flushCommand(GdbCommand &cmd)
{
    ++currentToken();
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
    if (cmd.command.contains(__("%1")))
        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);
}

void GdbEngine::sendCommand(const QString &command, GdbCommandFlags flags)
{
    execCommandInternal(command, flags, 0, 0, QVariant());
}

con's avatar
con committed
628
629
630
631
632
633
634
635
636
637
638
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
    //qDebug() << "TOKEN: " << record.token
    //    << " ACCEPTABLE: " << m_oldestAcceptableToken;
    //qDebug() << "";
    //qDebug() << "\nRESULT" << record.token << record.toString();

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

639
    GdbCommand cmd = m_cookieForToken.take(token);
con's avatar
con committed
640

641
    if (record.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
con's avatar
con committed
642
643
644
645
646
647
648
649
650
651
652
653
654
655
        //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
        //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
        return;
    }

#if 0
    qDebug() << "# handleOutput, "
        << "cmd type: " << cmd.type
        << "cmd synchronized: " << cmd.synchronized
        << "\n record: " << record.toString();
#endif

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

656
657
    if (cmd.callback)
        (this->*(cmd.callback))(record, cmd.cookie);
con's avatar
con committed
658

659
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
660
661
662
        --m_pendingRequests;
        PENDING_DEBUG("   TYPE " << cmd.type << " DECREMENTS PENDING TO: "
            << m_pendingRequests << cmd.command);
663
664
        if (m_pendingRequests <= 0) {
            PENDING_DEBUG(" ....  AND TRIGGERS MODEL UPDATE");
con's avatar
con committed
665
            updateWatchModel2();
666
        }
con's avatar
con committed
667
668
669
670
671
672
673
674
675
    } else {
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
            << m_pendingRequests << cmd.command);
    }
}

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

680
    m_gdbProc.write(command.toLocal8Bit() + "\r\n");
con's avatar
con committed
681
682
}

683
void GdbEngine::handleTargetCore(const GdbResultRecord &, const QVariant &)
684
685
{
    qq->notifyInferiorStopped();
686
687
688
689
690
691
692
693
694
695
    q->showStatusMessage(tr("Core file loaded."));

    q->resetLocation();

    //
    // Stack
    //
    qq->stackHandler()->setCurrentIndex(0);
    updateLocals(); // Quick shot

696
    reloadStack();
697
    if (supportsThreads())
698
        execCommandFC(_("-thread-list-ids"), handleStackListThreads, WatchUpdate, 0);
699
700
701
702
703
704
705
706
707
708
709
710

    //
    // Disassembler
    //
    // XXX we have no data here ...
    //m_address = data.findChild("frame").findChild("addr").data();
    //qq->reloadDisassembler();

    //
    // Registers
    //
    qq->reloadRegisters();
711

712
    // Gdb-Macro based DebuggingHelpers
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
713
    sendCommand(_(
714
715
716
717
718
719
720
721
722
        "define qdumpqstring\n"
        "set $i = 0\n"
        "set $l = $arg0->d->size\n"
        "set $p = $arg0->d->data\n"
        "while $i < $l\n"
        "printf \"%d \",$p[$i++]\n"
        "end\n"
        "printf \"\\n\"\n"
        "end\n"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
723
    ));
724

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
725
    sendCommand(_(
726
727
728
729
730
731
732
733
        "define qdumpqstringlist\n"
        "set $i = $arg0->d->begin\n"
        "set $e = $arg0->d->end\n"
        "while $i < $e\n"
        "printf \"%d \",$arg0->d->array + $i++\n"
        "end\n"
        "printf \"\\n\"\n"
        "end\n"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
734
    ));
735
736
}

737
#if 0
con's avatar
con committed
738
739
740
741
742
743
744
void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
{
    // FIXME: remove this special case as soon as 'pwd'
    // is supported by MI
    //qDebug() << "PWD OUTPUT:" <<  record.toString();
    // Gdb responses _unless_ we get an error first.
    if (record.resultClass == GdbResultDone) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
745
        QByteArray pwd = record.data.findChild("consolestreamoutput").data();
con's avatar
con committed
746
747
748
#ifdef Q_OS_LINUX
        // "5^done,{logstreamoutput="pwd ",consolestreamoutput
        // ="Working directory /home/apoenitz/dev/work/test1.  "}
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
749
750
751
752
753
        int pos = pwd.indexOf("Working directory");
        pwd = pwd.mid(pos + 18);
        pwd = pwd.trimmed();
        if (pwd.endsWith('.'))
            pwd.chop(1);
con's avatar
con committed
754
755
#endif
#ifdef Q_OS_WIN
756
        FIXME: this is broken
con's avatar
con committed
757
        // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
758
        pwd = pwd.trimmed();
con's avatar
con committed
759
#endif
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
760
        m_pwd = QString::fromLocal8Bit(pwd);
hjk's avatar
hjk committed
761
        debugMessage("PWD RESULT: " + m_pwd);
con's avatar
con committed
762
763
    }
}
764
#endif
con's avatar
con committed
765

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

793
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
794
795
796
{
    if (record.resultClass == GdbResultDone) {
        // FIXME: use something more robust
Roberto Raggi's avatar
Roberto Raggi committed
797
        // WIN:     * 3 Thread 2312.0x4d0  0x7c91120f in ?? ()
798
        // LINUX:   * 1 Thread 0x7f466273c6f0 (LWP 21455)  0x0000000000404542 in ...
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
799
800
        QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
        QString data = _(record.data.findChild("consolestreamoutput").data());
801
802
803
804
805
        if (re.indexIn(data) != -1)
            maybeHandleInferiorPidChanged(re.cap(1));
    }
}

806
void GdbEngine::handleInfoProc(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
807
808
809
810
{
    if (record.resultClass == GdbResultDone) {
        #if defined(Q_OS_MAC)
        //^done,process-id="85075"
Oswald Buddenhagen's avatar
nicer    
Oswald Buddenhagen committed
811
        maybeHandleInferiorPidChanged(_(record.data.findChild("process-id").data()));
con's avatar
con committed
812
813
814
815
        #endif

        #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
        // FIXME: use something more robust
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
816
817
        QRegExp re(__("process (\\d+)"));
        QString data = __(record.data.findChild("consolestreamoutput").data());
con's avatar
con committed
818
819
820
821
822
823
        if (re.indexIn(data) != -1)
            maybeHandleInferiorPidChanged(re.cap(1));
        #endif
    }
}

824
void GdbEngine::handleInfoShared(const GdbResultRecord &record, const QVariant &cookie)
con's avatar
con committed
825
826
827
{
    if (record.resultClass == GdbResultDone) {
        // let the modules handler do the parsing
828
        handleModulesList(record, cookie);
con's avatar
con committed
829
830
831
    }
}

832
#if 0
con's avatar
con committed
833
834
835
836
837
838
839
840
841
842
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();
843
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
844
    QByteArray output = record.data.findChild("logstreamoutput").data();
845
    if (output.isEmpty())
con's avatar
con committed
846
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
847
848
849
850
851
852
853
854
855
    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
856
}
857
#endif
con's avatar
con committed
858

859
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
860
861
862
863
864
865
866
867
{
    // 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();
868
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
con's avatar
con committed
869
    GdbMi frame = record.data.findChild("frame");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
870
    QString file = QString::fromLocal8Bit(frame.findChild("fullname").data());
con's avatar
con committed
871
872
873
874
875
876
    int line = frame.findChild("line").data().toInt();
    qDebug() << "HIT: " << file << line << " IN " << frame.toString()
        << " -- " << record.toString();
    q->gotoLocation(file, line, true);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
877
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
878
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
879
880
881
882
    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
883
884
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
885
static bool isStoppedReason(const QByteArray &reason)
con's avatar
con committed
886
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
887
888
889
890
891
892
893
    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"
894
        #ifdef Q_OS_MAC
con's avatar
con committed
895
        || reason.isEmpty()
896
        #endif
con's avatar
con committed
897
898
899
    ;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
900
901
902
void GdbEngine::handleAqcuiredInferior()
{
    #if defined(Q_OS_WIN)
903
    execCommand(_("info thread"), handleInfoThreads);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
904
905
    #endif
    #if defined(Q_OS_LINUX)
906
    execCommand(_("info proc"), handleInfoProc);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
907
908
    #endif
    #if defined(Q_OS_MAC)
909
    execCommandF(_("info pid"), handleInfoProc, NeedsStop);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
910
    #endif
911
    if (theDebuggerBoolSetting(ListSourceFiles))
912
        reloadSourceFiles();
913
    tryLoadDebuggingHelpers();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
914

915
    #ifndef Q_OS_MAC
916
    // intentionally after tryLoadDebuggingHelpers(),
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
917
    // otherwise we'd interupt solib loading.
hjk's avatar
hjk committed
918
    if (theDebuggerBoolSetting(AllPluginBreakpoints)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
919
920
921
        sendCommand(_("set auto-solib-add on"));
        sendCommand(_("set stop-on-solib-events 0"));
        sendCommand(_("sharedlibrary .*"));
hjk's avatar
hjk committed
922
    } else if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
923
924
925
        sendCommand(_("set auto-solib-add on"));
        sendCommand(_("set stop-on-solib-events 1"));
        sendCommand(_("sharedlibrary ")
hjk's avatar
hjk committed
926
927
          + theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
    } else if (theDebuggerBoolSetting(NoPluginBreakpoints)) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
928
        // should be like that already
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
929
930
        sendCommand(_("set auto-solib-add off"));
        sendCommand(_("set stop-on-solib-events 0"));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
931
    }
932
933
    #endif

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
934
935
    // nicer to see a bit of the world we live in
    reloadModules();
936
    attemptBreakpointSynchronization();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
937
938
}

939
940
941
942
943
944
void GdbEngine::handleAutoContinue(const GdbResultRecord &, const QVariant &)
{
    continueInferior();
    q->showStatusMessage(tr("Continuing after temporary stop."));
}

con's avatar
con committed
945
946
void GdbEngine::handleAsyncOutput(const GdbMi &data)
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
947
    const QByteArray &reason = data.findChild("reason").data();
con's avatar
con committed
948

949
950
    if (isExitedReason(reason)) {
        qq->notifyInferiorExited();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
951
        QString msg;
952
        if (reason == "exited") {
953
954
955
956
957
            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
958
        } else {
959
            msg = tr("Program exited normally");
960
961
        }
        q->showStatusMessage(msg);
962
        execCommand(_("-gdb-exit"), handleExit);
963
964
965
966
        return;
    }


967
968
969
    //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
    //!MAC: startSymbolName == data.findChild("frame").findChild("func")
    if (m_waitingForFirstBreakpointToBeHit) {
970
971
972
973
        // 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.

974
        qq->notifyInferiorStopped();
975
        m_waitingForFirstBreakpointToBeHit = false;
976
977
978
        //
        // this will "continue" if done
        m_waitingForBreakpointSynchronizationToContinue = true;
979
980
        //
        // that's the "early stop"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
981
        handleAqcuiredInferior();
982
        return;
983
984
    }

985
    if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
Roberto Raggi's avatar
Roberto Raggi committed
986
        QTC_ASSERT(q->status() == DebuggerInferiorStopRequested,
987
988
989
990
            qDebug() << "STATUS: " << q->status())
        qq->notifyInferiorStopped();
        q->showStatusMessage(tr("Temporarily stopped."));
        // FIXME: racy
991
992
        while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
            GdbCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
993
            debugMessage(_("RUNNING QUEUED COMMAND %1 %2")
994
995
                .arg(cmd.command).arg(_(cmd.callbackName)));
            flushCommand(cmd);
996
        }
997
        execCommand(_("p temporaryStop"), handleAutoContinue);
998
999
1000
1001
        q->showStatusMessage(tr("Handling queued commands."));
        return;
    }

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

1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
    // 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;
    }

1029
    //tryLoadDebuggingHelpers();
con's avatar
con committed
1030
1031
1032

    // jump over well-known frames
    static int stepCounter = 0;
hjk's avatar
hjk committed
1033
    if (theDebuggerBoolSetting(SkipKnownFrames)) {
con's avatar
con committed
1034
1035
        if (reason == "end-stepping-range" || reason == "function-finished") {
            GdbMi frame = data.findChild("frame");
hjk's avatar
hjk committed
1036
            //debugMessage(frame.toString());
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1037
1038
            m_currentFrame = _(frame.findChild("addr").data() + '%' +
                 frame.findChild("func").data() + '%');
con's avatar
con committed
1039

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1040
1041
            QString funcName = _(frame.findChild("func").data());
            QString fileName = QString::fromLocal8Bit(frame.findChild("file").data());
con's avatar
con committed
1042
            if (isLeavableFunction(funcName, fileName)) {
hjk's avatar
hjk committed
1043
                //debugMessage("LEAVING" + funcName);
con's avatar
con committed
1044
1045
1046
1047
1048
1049
                ++stepCounter;
                q->stepOutExec();
                //stepExec();
                return;
            }
            if (isSkippableFunction(funcName, fileName)) {
hjk's avatar
hjk committed
1050
                //debugMessage("SKIPPING" + funcName);
con's avatar
con committed
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
                ++stepCounter;
                q->stepExec();
                return;
            }
            //if (stepCounter)
            //    qDebug() << "STEPCOUNTER:" << stepCounter;
            stepCounter = 0;
        }
    }

    if (isStoppedReason(reason) || reason.isEmpty()) {
1062
        if (m_modulesListOutdated) {
1063
            reloadModules();
1064
            m_modulesListOutdated = false;
1065
        }
con's avatar
con committed
1066
1067
        // Need another round trip
        if (reason == "breakpoint-hit") {
1068
            q->showStatusMessage(tr("Stopped at breakpoint."));
con's avatar
con committed
1069
            GdbMi frame = data.findChild("frame");
hjk's avatar
hjk committed
1070
            //debugMessage("HIT BREAKPOINT: " + frame.toString());
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1071
1072
            m_currentFrame = _(frame.findChild("addr").data() + '%' +
                 frame.findChild("func").data() + '%');
con's avatar
con committed
1073

1074
            QApplication::alert(q->mainWindow(), 3000);
hjk's avatar
hjk committed
1075
            if (theDebuggerAction(ListSourceFiles)->value().toBool())
1076
                reloadSourceFiles();
1077
            execCommand(_("-break-list"), handleBreakList);
con's avatar
con committed
1078
            QVariant var = QVariant::fromValue<GdbMi>(data);
1079
            execCommandFC(_("p 0"), handleAsyncOutput2, NoFlags, var);  // dummy
con's avatar
con committed
1080
        } else {
1081
1082
1083
1084
#ifdef Q_OS_LINUX
            // For some reason, attaching to a stopped process causes *two* stops
            // when trying to continue (kernel 2.6.24-23-ubuntu).
            // Interestingly enough, on MacOSX no signal is delivered at all.
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
1085
            if (reason == "signal-received"
1086
1087
1088