gdbengine.cpp 142 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
26
** contact the sales department at http://www.qtsoftware.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"
con's avatar
con committed
37 38
#include "debuggerconstants.h"
#include "debuggermanager.h"
39
#include "debuggertooltip.h"
con's avatar
con committed
40 41 42 43 44 45 46 47 48
#include "gdbmi.h"
#include "procinterrupt.h"

#include "disassemblerhandler.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 60 61
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTime>
#include <QtCore/QTimer>
62
#include <QtCore/QTextStream>
con's avatar
con committed
63 64

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
75
#ifdef Q_OS_UNIX
con's avatar
con committed
76 77 78
#include <unistd.h>
#include <dlfcn.h>
#endif
hjk's avatar
hjk committed
79
#include <ctype.h>
con's avatar
con committed
80 81 82 83 84 85 86 87 88 89 90 91 92

using namespace Debugger;
using namespace Debugger::Internal;
using namespace Debugger::Constants;

Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);

//#define DEBUG_PENDING  1
//#define DEBUG_SUBITEM  1

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

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

con's avatar
con committed
100 101 102 103 104 105 106 107 108 109 110 111
static int &currentToken()
{
    static int token = 0;
    return token;
}

///////////////////////////////////////////////////////////////////////
//
// GdbEngine
//
///////////////////////////////////////////////////////////////////////

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

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

135
void GdbEngine::initializeConnections()
con's avatar
con committed
136 137
{
    // Gdb Process interaction
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(gdbProcError(QProcess::ProcessError)));
    connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readGdbStandardOutput()));
    connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readGdbStandardError()));
    connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
        q, SLOT(exitDebugger()));

    connect(&m_stubProc, SIGNAL(processError(QString)),
        this, SLOT(stubError(QString)));
    connect(&m_stubProc, SIGNAL(processStarted()),
        this, SLOT(stubStarted()));
    connect(&m_stubProc, SIGNAL(wrapperStopped()),
        q, SLOT(exitDebugger()));

    connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
        this, SLOT(uploadProcError(QProcess::ProcessError)));
156 157 158 159
    connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
        this, SLOT(readUploadStandardOutput()));
    connect(&m_uploadProc, SIGNAL(readyReadStandardError()),
        this, SLOT(readUploadStandardError()));
160

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

165 166
    connect(this, SIGNAL(gdbOutputAvailable(int,QString)),
        q, SLOT(showDebuggerOutput(int,QString)),
con's avatar
con committed
167
        Qt::QueuedConnection);
168 169
    connect(this, SIGNAL(gdbInputAvailable(int,QString)),
        q, SLOT(showDebuggerInput(int,QString)),
con's avatar
con committed
170
        Qt::QueuedConnection);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
171 172
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
        q, SLOT(showApplicationOutput(QString)),
con's avatar
con committed
173
        Qt::QueuedConnection);
174

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

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

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

    m_modulesListOutdated = true;
    m_oldestAcceptableToken = -1;
    m_outputCodec = QTextCodec::codecForLocale();
    m_pendingRequests = 0;
198
    m_autoContinue = false;
199
    m_waitingForFirstBreakpointToBeHit = false;
hjk's avatar
hjk committed
200
    m_commandsToRunOnTemporaryBreak.clear();
201
    m_cookieForToken.clear();
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    m_customOutputForToken.clear();

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

    m_inbuffer.clear();

    m_address.clear();
    m_currentFunctionArgs.clear();
    m_currentFrame.clear();
    m_dumperHelper = QtDumperHelper();

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

con's avatar
con committed
223 224 225
void GdbEngine::gdbProcError(QProcess::ProcessError error)
{
    QString msg;
226
    bool kill = true;
con's avatar
con committed
227 228
    switch (error) {
        case QProcess::FailedToStart:
229
            kill = false;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
230
            msg = tr("The Gdb process failed to start. Either the "
con's avatar
con committed
231
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
232
                "permissions to invoke the program.")
hjk's avatar
hjk committed
233
                .arg(theDebuggerStringSetting(GdbLocation));
con's avatar
con committed
234 235
            break;
        case QProcess::Crashed:
236
            kill = false;
con's avatar
con committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
            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().");
    }

259
    q->showStatusMessage(msg);
con's avatar
con committed
260 261
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
    // act as if it was closed by the core
262 263
    if (kill)
        q->exitDebugger();
con's avatar
con committed
264 265
}

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
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);
}

303 304 305
void GdbEngine::readUploadStandardOutput()
{
    QByteArray ba = m_uploadProc.readAllStandardOutput();
306
    gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(ba, ba.length()));
307 308 309 310 311
}

void GdbEngine::readUploadStandardError()
{
    QByteArray ba = m_uploadProc.readAllStandardError();
312
    gdbOutputAvailable(LogError, QString::fromLocal8Bit(ba, ba.length()));
313 314
}

con's avatar
con committed
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
#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

331 332 333 334 335 336
void GdbEngine::readDebugeeOutput(const QByteArray &data)
{
    emit applicationOutputAvailable(m_outputCodec->toUnicode(
            data.constData(), data.length(), &m_outputCodecState));
}

hjk's avatar
hjk committed
337 338
void GdbEngine::debugMessage(const QString &msg)
{
339
    emit gdbOutputAvailable(LogDebug, msg);
hjk's avatar
hjk committed
340 341
}

342
void GdbEngine::handleResponse(const QByteArray &buff)
con's avatar
con committed
343 344 345
{
    static QTime lastTime;

346 347 348
    if (theDebuggerBoolSetting(LogTimeStamps))
        emit gdbOutputAvailable(LogDebug, currentTime());
    emit gdbOutputAvailable(LogOutput, QString::fromLocal8Bit(buff, buff.length()));
con's avatar
con committed
349 350 351 352 353

#if 0
    qDebug() // << "#### start response handling #### "
        << currentTime()
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
354 355
        << "buf:" << buff.left(1500) << "..."
        //<< "buf:" << buff
356
        << "size:" << buff.size();
con's avatar
con committed
357
#else
358
    //qDebug() << "buf:" << buff;
con's avatar
con committed
359 360 361 362
#endif

    lastTime = QTime::currentTime();

363 364
    if (buff.isEmpty() || buff == "(gdb) ")
        return;
con's avatar
con committed
365

366 367 368
    const char *from = buff.constData();
    const char *to = from + buff.size();
    const char *inner;
con's avatar
con committed
369

370 371 372 373
    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
374
            break;
375 376 377
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
378
        //qDebug() << "found token" << token;
379
    }
con's avatar
con committed
380

381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
    // 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
396

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

472
        case '~': {
473 474 475
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingConsoleStreamOutput += data;
            if (data.startsWith("Reading symbols from ")) {
476
                q->showStatusMessage(tr("Reading %1...").arg(_(data.mid(21))));
477
            }
478 479
            break;
        }
480

481
        case '@': {
482 483
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingTargetStreamOutput += data;
484
            break;
con's avatar
con committed
485 486
        }

487 488 489 490 491 492
        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
493
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
494 495 496
            break;
        }

497 498
        case '^': {
            GdbResultRecord record;
con's avatar
con committed
499

500
            record.token = token;
con's avatar
con committed
501

502 503 504
            for (inner = from; inner != to; ++inner)
                if (*inner < 'a' || *inner > 'z')
                    break;
con's avatar
con committed
505

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
506
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
507 508 509 510 511 512 513 514 515 516 517 518
            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
519

520 521
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
522 523 524 525 526 527 528 529 530
                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
531 532
                }
            }
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557

            //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
558 559 560 561
        }
    }
}

562
void GdbEngine::handleStubAttached(const GdbResultRecord &, const QVariant &)
563 564 565
{
    qq->notifyInferiorStopped();
    handleAqcuiredInferior();
566
    m_autoContinue = true;
567 568 569 570
}

void GdbEngine::stubStarted()
{
571 572 573
    const qint64 attachedPID = m_stubProc.applicationPID();
    qq->notifyInferiorPidChanged(attachedPID);
    postCommand(_("attach %1").arg(attachedPID), CB(handleStubAttached));
574 575 576 577 578 579 580
}

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

con's avatar
con committed
581 582
void GdbEngine::readGdbStandardError()
{
583
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
con's avatar
con committed
584 585 586 587
}

void GdbEngine::readGdbStandardOutput()
{
588 589
    int newstart = 0;
    int scan = m_inbuffer.size();
con's avatar
con committed
590

591
    m_inbuffer.append(m_gdbProc.readAllStandardOutput());
con's avatar
con committed
592

593 594
    while (newstart < m_inbuffer.size()) {
        int start = newstart;
595
        int end = m_inbuffer.indexOf('\n', scan);
596 597 598 599 600
        if (end < 0) {
            m_inbuffer.remove(0, start);
            return;
        }
        newstart = end + 1;
601
        scan = newstart;
602 603
        if (end == start)
            continue;
604
        #if defined(Q_OS_WIN)
605 606 607 608 609
        if (m_inbuffer.at(end - 1) == '\r') {
            --end;
            if (end == start)
                continue;
        }
610
        #endif
611
        handleResponse(QByteArray::fromRawData(m_inbuffer.constData() + start, end - start));
con's avatar
con committed
612
    }
613
    m_inbuffer.clear();
con's avatar
con committed
614 615 616 617
}

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

hjk's avatar
hjk committed
620
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
621
        debugMessage(_("TRYING TO INTERRUPT INFERIOR WITHOUT RUNNING GDB"));
hjk's avatar
hjk committed
622
        qq->notifyInferiorExited();
con's avatar
con committed
623
        return;
hjk's avatar
hjk committed
624
    }
con's avatar
con committed
625

626
    if (q->startMode() == StartRemote) {
627
        postCommand(_("-exec-interrupt"));
628 629 630
        return;
    }

631 632
    const qint64 attachedPID = q->inferiorPid();
    if (attachedPID <= 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
633
        debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
con's avatar
con committed
634 635 636
        return;
    }

637 638
    if (!interruptProcess(attachedPID))
        debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
con's avatar
con committed
639 640 641 642
}

void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{
643
    const qint64 pid = pid0.toLongLong();
con's avatar
con committed
644
    if (pid == 0) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
645
        debugMessage(_("Cannot parse PID from %1").arg(pid0));
con's avatar
con committed
646 647
        return;
    }
648
    if (pid == q->inferiorPid())
con's avatar
con committed
649
        return;
650 651
    debugMessage(_("FOUND PID %1").arg(pid));    

Roberto Raggi's avatar
Roberto Raggi committed
652
    qq->notifyInferiorPidChanged(pid);
653 654
    if (m_dumperInjectionLoad)
        tryLoadDebuggingHelpers();
con's avatar
con committed
655 656
}

657
void GdbEngine::postCommand(const QString &command, GdbCommandCallback callback,
658 659
                            const char *callbackName, const QVariant &cookie)
{
660
    postCommand(command, NoFlags, callback, callbackName, cookie);
661 662
}

663
void GdbEngine::postCommand(const QString &command, GdbCommandFlags flags,
664 665
                            GdbCommandCallback callback, const char *callbackName,
                            const QVariant &cookie)
con's avatar
con committed
666 667
{
    if (m_gdbProc.state() == QProcess::NotRunning) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
668
        debugMessage(_("NO GDB PROCESS RUNNING, CMD IGNORED: ") + command);
con's avatar
con committed
669 670 671
        return;
    }

672
    if (flags & RebuildModel) {
con's avatar
con committed
673
        ++m_pendingRequests;
674
        PENDING_DEBUG("   CALLBACK" << callbackName << "INCREMENTS PENDING TO:"
con's avatar
con committed
675 676
            << m_pendingRequests << command);
    } else {
677
        PENDING_DEBUG("   UNKNOWN CALLBACK" << callbackName << "LEAVES PENDING AT:"
con's avatar
con committed
678 679 680
            << m_pendingRequests << command);
    }

681
    GdbCommand cmd;
con's avatar
con committed
682
    cmd.command = command;
683 684 685
    cmd.flags = flags;
    cmd.callback = callback;
    cmd.callbackName = callbackName;
con's avatar
con committed
686 687
    cmd.cookie = cookie;

688
    if ((flags & NeedsStop) && q->status() != DebuggerInferiorStopped
hjk's avatar
hjk committed
689 690 691
            && q->status() != DebuggerProcessStartingUp) {
        // queue the commands that we cannot send at once
        QTC_ASSERT(q->status() == DebuggerInferiorRunning,
692
            qDebug() << "STATUS:" << q->status());
hjk's avatar
hjk committed
693
        q->showStatusMessage(tr("Stopping temporarily."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
694
        debugMessage(_("QUEUING COMMAND ") + cmd.command);
hjk's avatar
hjk committed
695 696 697
        m_commandsToRunOnTemporaryBreak.append(cmd);
        interruptInferior();
    } else if (!command.isEmpty()) {
698
        flushCommand(cmd);
con's avatar
con committed
699 700 701
    }
}

702 703 704 705 706
void GdbEngine::flushCommand(GdbCommand &cmd)
{
    ++currentToken();
    m_cookieForToken[currentToken()] = cmd;
    cmd.command = QString::number(currentToken()) + cmd.command;
707
    if (cmd.flags & EmbedToken)
708 709 710 711 712
        cmd.command = cmd.command.arg(currentToken());

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

con's avatar
con committed
716 717
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
{
718 719
    //qDebug() << "TOKEN:" << record.token
    //    << " ACCEPTABLE:" << m_oldestAcceptableToken;
con's avatar
con committed
720 721 722 723 724 725 726
    //qDebug() << "";
    //qDebug() << "\nRESULT" << record.token << record.toString();

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

727
    GdbCommand cmd = m_cookieForToken.take(token);
con's avatar
con committed
728

729
    if (record.token < m_oldestAcceptableToken && (cmd.flags & Discardable)) {
730
        //qDebug() << "### SKIPPING OLD RESULT" << record.toString();
con's avatar
con committed
731 732 733 734 735
        //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
        return;
    }

#if 0
736
    qDebug() << "# handleOutput,"
hjk's avatar
hjk committed
737
        << "cmd name:" << cmd.callbackName
738
        << " cmd synchronized:" << cmd.synchronized
con's avatar
con committed
739 740 741 742 743
        << "\n record: " << record.toString();
#endif

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

744 745
    if (cmd.callback)
        (this->*(cmd.callback))(record, cmd.cookie);
con's avatar
con committed
746

747
    if (cmd.flags & RebuildModel) {
con's avatar
con committed
748
        --m_pendingRequests;
hjk's avatar
hjk committed
749
        PENDING_DEBUG("   TYPE " << cmd.callbackName << " DECREMENTS PENDING TO: "
con's avatar
con committed
750
            << m_pendingRequests << cmd.command);
751
        if (m_pendingRequests <= 0) {
hjk's avatar
hjk committed
752 753
            PENDING_DEBUG("\n\n ....  AND TRIGGERS MODEL UPDATE\n");
            rebuildModel();
754
        }
con's avatar
con committed
755
    } else {
hjk's avatar
hjk committed
756
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.callbackName << " LEAVES PENDING AT: "
con's avatar
con committed
757 758
            << m_pendingRequests << cmd.command);
    }
759 760 761 762 763 764 765 766 767 768

    // 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
769 770 771 772 773
}

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

778
    m_gdbProc.write(command.toLocal8Bit() + "\r\n");
con's avatar
con committed
779 780
}

781
void GdbEngine::handleTargetCore(const GdbResultRecord &, const QVariant &)
782 783
{
    qq->notifyInferiorStopped();
784 785
    q->showStatusMessage(tr("Core file loaded."));
    q->resetLocation();
786
    tryLoadDebuggingHelpers();
787 788
    qq->stackHandler()->setCurrentIndex(0);
    updateLocals(); // Quick shot
789
    reloadStack();
790
    if (supportsThreads())
791
        postCommand(_("-thread-list-ids"), WatchUpdate, CB(handleStackListThreads), 0);
792
    qq->reloadRegisters();
793 794
}

795
void GdbEngine::handleQuerySources(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
796 797
{
    if (record.resultClass == GdbResultDone) {
798
        QMap<QString, QString> oldShortToFull = m_shortToFullName;
con's avatar
con committed
799 800 801 802 803 804
        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
805
            QString fileName = QString::fromLocal8Bit(item.findChild("file").data());
con's avatar
con committed
806
            GdbMi fullName = item.findChild("fullname");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
807
            QString full = QString::fromLocal8Bit(fullName.data());
con's avatar
con committed
808 809 810 811
            #ifdef Q_OS_WIN
            full = QDir::cleanPath(full);
            #endif
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
812
                //qDebug() << "STORING 2:" << fileName << full;
con's avatar
con committed
813 814 815 816
                m_shortToFullName[fileName] = full;
                m_fullToShortName[full] = fileName;
            }
        }
817 818
        if (m_shortToFullName != oldShortToFull)
            qq->sourceFileWindow()->setSourceFiles(m_shortToFullName);
con's avatar
con committed
819 820 821
    }
}

822
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
823
{
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
    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;
        }
841
    }
842 843 844 845 846
    // check "* 3 Thread ..."
    QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
    Q_ASSERT(re.isValid());
    if (re.indexIn(data) != -1)
        maybeHandleInferiorPidChanged(re.cap(1));
847 848
}

849
void GdbEngine::handleInfoProc(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
850 851
{
    if (record.resultClass == GdbResultDone) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
852
        #ifdef Q_OS_MAC
con's avatar
con committed
853
        //^done,process-id="85075"
Oswald Buddenhagen's avatar
nicer  
Oswald Buddenhagen committed
854
        maybeHandleInferiorPidChanged(_(record.data.findChild("process-id").data()));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
855
        #else
con's avatar
con committed
856
        // FIXME: use something more robust
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
857 858
        QRegExp re(__("process (\\d+)"));
        QString data = __(record.data.findChild("consolestreamoutput").data());
con's avatar
con committed
859 860 861 862 863 864
        if (re.indexIn(data) != -1)
            maybeHandleInferiorPidChanged(re.cap(1));
        #endif
    }
}

865
void GdbEngine::handleInfoShared(const GdbResultRecord &record, const QVariant &cookie)
con's avatar
con committed
866 867 868
{
    if (record.resultClass == GdbResultDone) {
        // let the modules handler do the parsing
869
        handleModulesList(record, cookie);
con's avatar
con committed
870 871 872
    }
}

873
#if 0
con's avatar
con committed
874 875 876 877 878 879 880 881 882 883
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();
884
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
885
    QByteArray output = record.data.findChild("logstreamoutput").data();
886
    if (output.isEmpty())
con's avatar
con committed
887
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
888 889 890 891 892 893 894 895 896
    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
897
}
898
#endif
con's avatar
con committed
899

900
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
901 902 903 904 905 906 907 908
{
    // 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();
909
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
con's avatar
con committed
910
    GdbMi frame = record.data.findChild("frame");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
911
    QString file = QString::fromLocal8Bit(frame.findChild("fullname").data());
con's avatar
con committed
912
    int line = frame.findChild("line").data().toInt();
913 914
    qDebug() << "HIT:" << file << line << "IN" << frame.toString()
        << "--" << record.toString();
con's avatar
con committed
915 916 917
    q->gotoLocation(file, line, true);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
918
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
919
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
920 921 922 923
    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
924 925
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
926
static bool isStoppedReason(const QByteArray &reason)
con's avatar
con committed
927
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
928 929 930 931 932 933 934
    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"
935
        #ifdef Q_OS_MAC
con's avatar
con committed
936
        || reason.isEmpty()
937
        #endif
con's avatar
con committed
938 939 940
    ;
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
941 942
void GdbEngine::handleAqcuiredInferior()
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
943
    #ifdef Q_OS_WIN
944
    postCommand(_("info thread"), CB(handleInfoThreads));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
945
    #elif defined(Q_OS_MAC)
946
    postCommand(_("info pid"), NeedsStop, CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
947 948
    #else
    postCommand(_("info proc"), CB(handleInfoProc));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
949
    #endif
950
    if (theDebuggerBoolSetting(ListSourceFiles))
951
        reloadSourceFiles();
952

953
    // Reverse debugging. FIXME: Should only be used when available.
954 955
    //if (theDebuggerBoolSetting(EnableReverseDebugging))
    //    postCommand(_("target record"));
956

957
    tryLoadDebuggingHelpers();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
958

959
    #ifndef Q_OS_MAC
960
    // intentionally after tryLoadDebuggingHelpers(),
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
961
    // otherwise we'd interupt solib loading.
hjk's avatar
hjk committed
962
    if (theDebuggerBoolSetting(AllPluginBreakpoints)) {
963 964 965
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 0"));
        postCommand(_("sharedlibrary .*"));
hjk's avatar
hjk committed
966
    } else if (theDebuggerBoolSetting(SelectedPluginBreakpoints)) {
967 968 969
        postCommand(_("set auto-solib-add on"));
        postCommand(_("set stop-on-solib-events 1"));
        postCommand(_("sharedlibrary ")
hjk's avatar
hjk committed
970 971
          + theDebuggerStringSetting(SelectedPluginBreakpointsPattern));
    } else if (theDebuggerBoolSetting(NoPluginBreakpoints)) {</