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

30 31
#define QT_NO_CAST_FROM_ASCII

con's avatar
con committed
32
#include "gdbengine.h"
33
#include "gdboptionspage.h"
con's avatar
con committed
34

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

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

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

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

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

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
76
#ifdef Q_OS_UNIX
con's avatar
con committed
77 78 79
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
80
#include <ctype.h>
con's avatar
con committed
81

82 83
namespace Debugger {
namespace Internal {
con's avatar
con committed
84 85 86
using namespace Debugger::Constants;

//#define DEBUG_PENDING  1
hjk's avatar
hjk committed
87
//#define DEBUG_SUBITEM  1
con's avatar
con committed
88 89 90 91

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

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

con's avatar
con committed
99 100 101 102 103 104
static int &currentToken()
{
    static int token = 0;
    return token;
}

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
// reads a MI-encoded item frome the consolestream
static bool parseConsoleStream(const GdbResultRecord &record, GdbMi *contents)
{
    GdbMi output = record.data.findChild("consolestreamoutput");
    QByteArray out = output.data();

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

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

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

static QByteArray parsePlainConsoleStream(const GdbResultRecord &record)
{
    GdbMi output = record.data.findChild("consolestreamoutput");
    QByteArray out = output.data();
    // FIXME: proper decoding needed
    if (out.endsWith("\\n"))
        out.chop(2);
    while (out.endsWith('\n') || out.endsWith(' '))
        out.chop(1);
    int pos = out.indexOf(" = ");
    return out.mid(pos + 3);
}

hjk's avatar
hjk committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154
///////////////////////////////////////////////////////////////////////
//
// GdbProcess
//
///////////////////////////////////////////////////////////////////////

void GdbProcess::attach(GdbEngine *engine) const
{
    QFileInfo fi(engine->startParameters().executable);
    QString fileName = fi.absoluteFilePath();
    engine->postCommand(_("-file-exec-and-symbols ") + fileName,
        &GdbEngine::handleFileExecAndSymbols, "handleFileExecAndSymbols");
}

con's avatar
con committed
155 156 157 158 159 160
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

161
GdbEngine::GdbEngine(DebuggerManager *parent, GdbProcessBase *gdbProc) :
162
#ifdef Q_OS_WIN // Do injection loading with MinGW (call loading does not work with 64bit)
163
    m_dumperInjectionLoad(true),
164
#else
165
    m_dumperInjectionLoad(false),
166
#endif
167
    q(parent),
168
    qq(parent->engineInterface())
con's avatar
con committed
169
{
170
    m_gdbProc = gdbProc;
171
    m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
172 173 174
#ifdef Q_OS_UNIX
    m_stubProc.setSettings(Core::ICore::instance()->settings());
#endif
175 176
    initializeVariables();
    initializeConnections();
con's avatar
con committed
177 178 179 180
}

GdbEngine::~GdbEngine()
{
hjk's avatar
hjk committed
181
    // prevent sending error messages afterwards
182 183
    m_gdbProc->disconnect(this);
    delete m_gdbProc;
con's avatar
con committed
184 185
}

186
void GdbEngine::initializeConnections()
con's avatar
con committed
187 188
{
    // Gdb Process interaction
189
    connect(m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
190
        this, SLOT(gdbProcError(QProcess::ProcessError)));
191
    connect(m_gdbProc, SIGNAL(readyReadStandardOutput()),
192
        this, SLOT(readGdbStandardOutput()));
193
    connect(m_gdbProc, SIGNAL(readyReadStandardError()),
194
        this, SLOT(readGdbStandardError()));
195
    connect(m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
196
        q, SLOT(exitDebugger()));
hjk's avatar
hjk committed
197 198
    connect(m_gdbProc, SIGNAL(started()),
        this, SLOT(startDebugger2()));
199 200 201 202 203 204 205 206 207 208

    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)));
209 210 211 212
    connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readUploadStandardOutput()));
    connect(&m_uploadProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readUploadStandardError()));
213

con's avatar
con committed
214
    // Output
215
    connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
216
        this, SLOT(readDebugeeOutput(QByteArray)));
con's avatar
con committed
217

218 219
    connect(this, SIGNAL(gdbOutputAvailable(int,QString)),
        q, SLOT(showDebuggerOutput(int,QString)),
con's avatar
con committed
220
        Qt::QueuedConnection);
221 222
    connect(this, SIGNAL(gdbInputAvailable(int,QString)),
        q, SLOT(showDebuggerInput(int,QString)),
con's avatar
con committed
223
        Qt::QueuedConnection);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
224 225
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        q, SLOT(showApplicationOutput(QString)),
con's avatar
con committed
226
        Qt::QueuedConnection);
227

228
    // FIXME: These trigger even if the engine is not active
229 230 231 232 233 234
    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
235 236
}

237 238
void GdbEngine::initializeVariables()
{
239
    m_debuggingHelperState = DebuggingHelperUninitialized;
240
    m_gdbVersion = 100;
hjk's avatar
hjk committed
241
    m_gdbBuildVersion = -1;
242 243 244 245 246 247 248 249 250

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
251
    m_autoContinue = false;
252
    m_waitingForFirstBreakpointToBeHit = false;
hjk's avatar
hjk committed
253
    m_commandsToRunOnTemporaryBreak.clear();
254
    m_cookieForToken.clear();
255 256 257 258 259 260 261 262 263 264
    m_customOutputForToken.clear();

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

    m_inbuffer.clear();

    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
265
    m_dumperHelper.clear();
266 267 268 269 270 271 272

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

con's avatar
con committed
275 276 277
void GdbEngine::gdbProcError(QProcess::ProcessError error)
{
    QString msg;
278
    bool kill = true;
con's avatar
con committed
279 280
    switch (error) {
        case QProcess::FailedToStart:
281
            kill = false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
282
            msg = tr("The Gdb process failed to start. Either the "
con's avatar
con committed
283
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
284
                "permissions to invoke the program.")
hjk's avatar
hjk committed
285
                .arg(theDebuggerStringSetting(GdbLocation));
286
            emitStartFailed();
con's avatar
con committed
287 288
            break;
        case QProcess::Crashed:
289
            kill = false;
con's avatar
con committed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
            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().");
    }

312
    q->showStatusMessage(msg);
con's avatar
con committed
313 314
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
    // act as if it was closed by the core
315 316
    if (kill)
        q->exitDebugger();
con's avatar
con committed
317 318
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
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);
}

356 357 358
void GdbEngine::readUploadStandardOutput()
{
    QByteArray ba = m_uploadProc.readAllStandardOutput();
359
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(ba, ba.length()));
360 361 362 363 364
}

void GdbEngine::readUploadStandardError()
{
    QByteArray ba = m_uploadProc.readAllStandardError();
365
    gdbOutputAvailable(LogError, QString::fromLocal8Bit(ba, ba.length()));
366 367
}

con's avatar
con committed
368 369 370 371
#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
372
    Q_UNUSED(to)
con's avatar
con committed
373 374 375 376 377 378 379 380 381 382 383
    // 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

384 385 386 387 388 389
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
    emit applicationOutputAvailable(m_outputCodec->toUnicode(
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
390 391
void GdbEngine::debugMessage(const QString &msg)
{
392
    emit gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
393 394
}

395
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
396 397 398
{
    static QTime lastTime;

399
    if (theDebuggerBoolSetting(LogTimeStamps))
400
        emit gdbOutputAvailable(LogTime, currentTime());
401
    emit gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
402 403 404 405 406

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
407 408
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
409
        << "size:" << buff.size();
con's avatar
con committed
410
#else
411
    //qDebug() << "buf:" << buff;
con's avatar
con committed
412 413 414 415
#endif

    lastTime = QTime::currentTime();

416 417
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
418

419 420 421
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
422

423 424 425 426
    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
427
            break;
428 429 430
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
431
        //qDebug() << "found token" << token;
432
    }
con's avatar
con committed
433

434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
    // 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
449

450 451 452
            GdbMi record;
            while (from != to) {
                GdbMi data;
hjk's avatar
hjk committed
453
                if (*from != ',') {
hjk's avatar
hjk committed
454 455
                    // happens on archer where we get 
                    // 23^running <NL> *running,thread-id="all" <NL> (gdb) 
456
                    record.m_type = GdbMi::Tuple;
hjk's avatar
hjk committed
457 458 459 460 461
                    break;
                }
                ++from; // skip ','
                data.parseResultOrValue(from, to);
                if (data.isValid()) {
462
                    //qDebug() << "parsed response:" << data.toString();
hjk's avatar
hjk committed
463 464
                    record.m_children += data;
                    record.m_type = GdbMi::Tuple;
con's avatar
con committed
465 466
                }
            }
467 468 469 470
            if (asyncClass == "stopped") {
                handleAsyncOutput(record);
            } else if (asyncClass == "running") {
                // Archer has 'thread-id="all"' here
471 472 473 474
            } 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",
475
                // symbols-loaded="0"
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
476
                QByteArray id = record.findChild("id").data();
477
                if (!id.isEmpty())
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
478
                    q->showStatusMessage(tr("Library %1 loaded.").arg(_(id)));
hjk's avatar
hjk committed
479 480 481 482
            } 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
483 484
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Library %1 unloaded.").arg(_(id)));
485 486
            } else if (asyncClass == "thread-group-created") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
487 488
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 created.").arg(_(id)));
489 490
            } else if (asyncClass == "thread-created") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
491 492
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 created.").arg(_(id)));
493 494
            } else if (asyncClass == "thread-group-exited") {
                // Archer has "{id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
495 496
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread group %1 exited.").arg(_(id)));
497 498
            } else if (asyncClass == "thread-exited") {
                //"{id="1",group-id="28902"}" 
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
499 500
                QByteArray id = record.findChild("id").data();
                QByteArray groupid = record.findChild("group-id").data();
501
                q->showStatusMessage(tr("Thread %1 in group %2 exited.")
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
502
                    .arg(_(id)).arg(_(groupid)));
hjk's avatar
hjk committed
503
            } else if (asyncClass == "thread-selected") {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
504 505
                QByteArray id = record.findChild("id").data();
                q->showStatusMessage(tr("Thread %1 selected.").arg(_(id)));
hjk's avatar
hjk committed
506
                //"{id="2"}" 
507
            #if defined(Q_OS_MAC)
508 509 510 511 512 513 514 515 516 517 518
            } 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 {
519
                qDebug() << "IGNORED ASYNC OUTPUT"
520
                    << asyncClass << record.toString();
521
            }
522 523
            break;
        }
524

525
        case '~': {
526 527 528
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
            if (data.startsWith("Reading symbols from ")) {
529
                q->showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))));
530
            }
531 532
            break;
        }
533

534
        case '@': {
535 536
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingTargetStreamOutput += data;
537
            break;
con's avatar
con committed
538 539
        }

540 541 542 543 544 545
        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
546
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
547 548 549
            break;
        }

550 551
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
552

553
            record.token = token;
con's avatar
con committed
554

555 556 557
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
558

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
559
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
560 561 562 563 564 565 566 567 568 569 570 571
            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
572

573 574
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
575 576 577 578 579 580 581 582 583
                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
584 585
                }
            }
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610

            //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
611 612 613 614
        }
    }
}

615
void GdbEngine::handleStubAttached(const GdbResultRecord &, const QVariant &)
616 617 618
{
    qq->notifyInferiorStopped();
    handleAqcuiredInferior();
619
    m_autoContinue = true;
620 621 622 623
}

void GdbEngine::stubStarted()
{
624 625 626
    const qint64 attachedPID = m_stubProc.applicationPID();
    qq->notifyInferiorPidChanged(attachedPID);
    postCommand(_("attach %1").arg(attachedPID), CB(handleStubAttached));
627 628 629 630 631 632 633
}

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

con's avatar
con committed
634 635
void GdbEngine::readGdbStandardError()
{
636
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc->readAllStandardError();
con's avatar
con committed
637 638 639 640
}

void GdbEngine::readGdbStandardOutput()
{
641 642
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
643

644
    m_inbuffer.append(m_gdbProc->readAllStandardOutput());
con's avatar
con committed
645

646 647
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
648
        int end = m_inbuffer.indexOf('\n', scan);
649 650 651 652 653
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
654
        scan = newstart;
655 656
        if (end == start)
            continue;
657
        #if defined(Q_OS_WIN)
658 659 660 661 662
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
663
        #endif
664
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
665
    }
666
    m_inbuffer.clear();
con's avatar
con committed
667 668 669 670
}

void GdbEngine::interruptInferior()
{
hjk's avatar
hjk committed
671
    qq->notifyInferiorStopRequested();
672

673
    if (m_gdbProc->state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
674
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
hjk's avatar
hjk committed
675
        qq->notifyInferiorExited();
con's avatar
con committed
676
        return;
hjk's avatar
hjk committed
677
    }
con's avatar
con committed
678

679
    if (q->startMode() == StartRemote) {
680
        postCommand(_("-exec-interrupt"));
681 682 683
        return;
    }

684 685
    const qint64 attachedPID = q->inferiorPid();
    if (attachedPID <= 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
686
        debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
con's avatar
con committed
687 688 689
        return;
    }

690 691
    if (!interruptProcess(attachedPID))
        debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
con's avatar
con committed
692 693 694 695
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
696
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
697
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
698
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
699 700
        return;
    }
701
    if (pid == q->inferiorPid())
con's avatar
con committed
702
        return;
703 704
    debugMessage(_("FOUND PID %1").arg(pid));    

Roberto Raggi's avatar
Roberto Raggi committed
705
    qq->notifyInferiorPidChanged(pid);
706 707
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
708 709
}

710
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
711 712
                            const char *callbackName, const QVariant &cookie)
{
713
    postCommand(command, NoFlags, callback, callbackName, cookie);
714 715
}

716
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
717 718
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
con's avatar
con committed
719
{
720
    if (m_gdbProc->state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
721
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + command);
con's avatar
con committed
722 723 724
        return;
    }

725
    if (flags & RebuildModel) {
con's avatar
con committed
726
        ++m_pendingRequests;
727
        PENDING_DEBUG("   CALLBACK" << callbackName << "INCREMENTS PENDING TO:"
con's avatar
con committed
728 729
            << m_pendingRequests << command);
    } else {
730
        PENDING_DEBUG("   UNKNOWN CALLBACK" << callbackName << "LEAVES PENDING AT:"
con's avatar
con committed
731 732 733
            << m_pendingRequests << command);
    }

734
    GdbCommand cmd;
con's avatar
con committed
735
    cmd.command = command;
736 737 738
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
con's avatar
con committed
739 740
    cmd.cookie = cookie;

741
    if ((flags & NeedsStop) && q->status() != DebuggerInferiorStopped
hjk's avatar
hjk committed
742 743 744
            && q->status() != DebuggerProcessStartingUp) {
        // queue the commands that we cannot send at once
        QTC_ASSERT(q->status() == DebuggerInferiorRunning,
745
            qDebug() << "STATUS:" << q->status());
hjk's avatar
hjk committed
746
        q->showStatusMessage(tr("Stopping temporarily."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
747
        debugMessage(_("QUEUING COMMAND ") + cmd.command);
hjk's avatar
hjk committed
748 749 750
        m_commandsToRunOnTemporaryBreak.append(cmd);
        interruptInferior();
    } else if (!command.isEmpty()) {
751
        flushCommand(cmd);
con's avatar
con committed
752 753 754
    }
}

755 756 757
void GdbEngine::flushCommand(GdbCommand &cmd)
{
    ++currentToken();
758
    cmd.postTime = QTime::currentTime();
759 760
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
761
    if (cmd.flags & EmbedToken)
762 763
        cmd.command = cmd.command.arg(currentToken());

764
    emit gdbInputAvailable(LogInput, cmd.command);
765
    executeDebuggerCommand(cmd.command);
766 767
}

con's avatar
con committed
768 769
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
770 771
    //qDebug() << "TOKEN:" << record.token
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
con's avatar
con committed
772 773 774 775 776 777 778
    //qDebug() << "";
    //qDebug() << "\nRESULT" << record.token << record.toString();

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

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797
    if (!m_cookieForToken.contains(token)) {
        // In theory this should not happen, in practice it does.
        debugMessage(_("COOKIE FOR TOKEN %1 ALREADY EATEN. "
            "TWO RESPONSES FOR ONE COMMAND?").arg(token));
        // handle a case known to occur on Linux/gdb 6.8 when debugging moc
        // with helpers enabled. In this case we get a second response with
        // msg="Cannot find new threads: generic error"
        if (record.resultClass == GdbResultError) {
            QByteArray msg = record.data.findChild("msg").data();
            QMessageBox::critical(q->mainWindow(), tr("Error"),
                tr("Executable failed:\n") + QString::fromLocal8Bit(msg));
            q->showStatusMessage(tr("Process failed to start."));
            exitDebugger();
            //qq->notifyInferiorStopped();
            //qq->notifyInferiorExited();
        }
        return;
    }

798
    GdbCommand cmd = m_cookieForToken.take(token);
799 800 801 802 803
    if (theDebuggerBoolSetting(LogTimeStamps)) {
        emit gdbOutputAvailable(LogTime, _("Response time: %1: %2 s")
            .arg(cmd.command)
            .arg(cmd.postTime.msecsTo(QTime::currentTime()) / 1000.));
    }
con's avatar
con committed
804

805
    if (record.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
806
        //qDebug() << "### SKIPPING OLD RESULT" << record.toString();
807
        //QMessageBox::information(q->mainWindow(), tr("Skipped"), "xxx");
con's avatar
con committed
808 809 810 811
        return;
    }

#if 0
812
    qDebug() << "# handleOutput,"
hjk's avatar
hjk committed
813
        << "cmd name:" << cmd.callbackName
814
        << " cmd synchronized:" << cmd.synchronized
con's avatar
con committed
815 816 817 818 819
        << "\n record: " << record.toString();
#endif

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

820 821
    if (cmd.callback)
        (this->*(cmd.callback))(record, cmd.cookie);
con's avatar
con committed
822

823
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
824
        --m_pendingRequests;
hjk's avatar
hjk committed
825
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
826
            << m_pendingRequests << cmd.command);
827
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
828 829
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
830
        }
con's avatar
con committed
831
    } else {
hjk's avatar
hjk committed
832
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
833 834
            << m_pendingRequests << cmd.command);
    }
835 836 837 838 839 840 841 842 843 844

    // 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
845 846 847 848
}

void GdbEngine::executeDebuggerCommand(const QString &command)
{
849 850
    if (m_gdbProc->state() != QProcess::Running) {
        debugMessage(_("GDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command);
con's avatar
con committed
851 852 853
        return;
    }

854
    m_gdbProc->write(command.toLatin1() + "\r\n");
con's avatar
con committed
855 856
}

857
void GdbEngine::handleTargetCore(const GdbResultRecord &, const QVariant &)
858 859
{
    qq->notifyInferiorStopped();
860 861
    q->showStatusMessage(tr("Core file loaded."));
    q->resetLocation();
862
    tryLoadDebuggingHelpers();
863 864
    qq->stackHandler()->setCurrentIndex(0);
    updateLocals(); // Quick shot
865
    reloadStack();
866
    if (supportsThreads())
867
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
868
    qq->reloadRegisters();
869 870
}

871
void GdbEngine::handleQuerySources(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
872 873
{
    if (record.resultClass == GdbResultDone) {
874
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
875 876 877 878 879 880
        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
881
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
882
            GdbMi fullName = item.findChild("fullname");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
883
            QString full = QString::fromLocal8Bit(fullName.data());
con's avatar
con committed
884 885 886 887
            #ifdef Q_OS_WIN
            full = QDir::cleanPath(full);
            #endif
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
888
                //qDebug() << "STORING 2:" << fileName << full;
con's avatar
con committed
889 890 891 892
                m_shortToFullName[fileName] = full;
                m_fullToShortName[full] = fileName;
            }
        }
893 894
        if (m_shortToFullName != oldShortToFull)
            qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
con's avatar
con committed
895 896 897
    }
}

898
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
899
{
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
    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;
        }
917
    }
918 919 920 921 922
    // check "* 3 Thread ..."
    QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
    Q_ASSERT(re.isValid());
    if (re.indexIn(data) != -1)
        maybeHandleInferiorPidChanged(re.cap(1));
923 924
}

925
void GdbEngine::handleInfoProc(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
926 927
{
    if (record.resultClass == GdbResultDone) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
928
        #ifdef Q_OS_MAC
con's avatar
con committed
929
        //^done,process-id="85075"
Oswald Buddenhagen's avatar
nicer  
Oswald Buddenhagen committed
930
        maybeHandleInferiorPidChanged(_(record.data.findChild("process-id").data()));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
931
        #else
con's avatar
con committed
932
        // FIXME: use something more robust
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
933 934
        QRegExp re(__("process (\\d+)"));
        QString data = __(record.data.findChild("consolestreamoutput").data());
con's avatar
con committed
935 936 937 938 939 940
        if (re.indexIn(data) != -1)
            maybeHandleInferiorPidChanged(re.cap(1));
        #endif
    }
}

941
void GdbEngine::handleInfoShared(const GdbResultRecord &record, const QVariant &cookie)
con's avatar
con committed
942 943 944
{
    if (record.resultClass == GdbResultDone) {
        // let the modules handler do the parsing
945
        handleModulesList(record, cookie);
con's avatar
con committed
946 947 948
    }
}

949
#if 0
con's avatar
con committed
950 951 952 953 954 955 956 957 958 959
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();
960
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
961
    QByteArray output = record.data.findChild("logstreamoutput").data();
962
    if (output.isEmpty())
con's avatar
con committed
963
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
964 965 966 967 968 969 970 971 972
    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
973
}
974
#endif
con's avatar
con committed
975

976
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record,<