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

30 31
#define QT_NO_CAST_FROM_ASCII

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

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

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

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

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

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

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

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

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

Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);

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

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

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

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

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

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

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

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

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

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

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

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

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

174
    // FIXME: These trigger even if the engine is not active
175 176 177 178 179 180
    connect(theDebuggerAction(UseDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseDebuggingHelpers(QVariant)));
    connect(theDebuggerAction(DebugDebuggingHelpers), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setDebugDebuggingHelpers(QVariant)));
    connect(theDebuggerAction(RecheckDebuggingHelpers), SIGNAL(triggered()),
        this, SLOT(recheckDebuggingHelperAvailability()));
con's avatar
con committed
181 182
}

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

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

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

con's avatar
con committed
202 203 204 205 206
void GdbEngine::gdbProcError(QProcess::ProcessError error)
{
    QString msg;
    switch (error) {
        case QProcess::FailedToStart:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
207
            msg = tr("The Gdb process failed to start. Either the "
con's avatar
con committed
208
                "invoked program '%1' is missing, or you may have insufficient "
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
209
                "permissions to invoke the program.")
hjk's avatar
hjk committed
210
                .arg(theDebuggerStringSetting(GdbLocation));
con's avatar
con committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
            break;
        case QProcess::Crashed:
            msg = tr("The Gdb process crashed some time after starting "
                "successfully.");
            break;
        case QProcess::Timedout:
            msg = tr("The last waitFor...() function timed out. "
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
            break;
        case QProcess::WriteError:
            msg = tr("An error occurred when attempting to write "
                "to the Gdb process. For example, the process may not be running, "
                "or it may have closed its input channel.");
            break;
        case QProcess::ReadError:
            msg = tr("An error occurred when attempting to read from "
                "the Gdb process. For example, the process may not be running.");
            break;
        default:
            msg = tr("An unknown error in the Gdb process occurred. "
                "This is the default return value of error().");
    }

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

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
void GdbEngine::uploadProcError(QProcess::ProcessError error)
{
    QString msg;
    switch (error) {
        case QProcess::FailedToStart:
            msg = tr("The upload process failed to start. Either the "
                "invoked script '%1' is missing, or you may have insufficient "
                "permissions to invoke the program.")
                .arg(theDebuggerStringSetting(GdbLocation));
            break;
        case QProcess::Crashed:
            msg = tr("The upload process crashed some time after starting "
                "successfully.");
            break;
        case QProcess::Timedout:
            msg = tr("The last waitFor...() function timed out. "
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
            break;
        case QProcess::WriteError:
            msg = tr("An error occurred when attempting to write "
                "to the upload process. For example, the process may not be running, "
                "or it may have closed its input channel.");
            break;
        case QProcess::ReadError:
            msg = tr("An error occurred when attempting to read from "
                "the upload process. For example, the process may not be running.");
            break;
        default:
            msg = tr("An unknown error in the upload process occurred. "
                "This is the default return value of error().");
    }

    q->showStatusMessage(msg);
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
}

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

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

con's avatar
con committed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
#if 0
static void dump(const char *first, const char *middle, const QString & to)
{
    QByteArray ba(first, middle - first);
    Q_UNUSED(to);
    // note that qDebug cuts off output after a certain size... (bug?)
    qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
        qPrintable(currentTime()),
        qPrintable(QString(ba).trimmed()),
        qPrintable(to.trimmed()));
    //qDebug() << "";
    //qDebug() << qPrintable(currentTime())
    //    << " Reading response:  " << QString(ba).trimmed() << "\n";
}
#endif

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

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

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

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

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

    lastTime = QTime::currentTime();

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

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

344 345 346 347
    int token = -1;
    // token is a sequence of numbers
    for (inner = from; inner != to; ++inner)
        if (*inner < '0' || *inner > '9')
con's avatar
con committed
348
            break;
349 350 351
    if (from != inner) {
        token = QByteArray(from, inner - from).toInt();
        from = inner;
352
        //qDebug() << "found token" << token;
353
    }
con's avatar
con committed
354

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
    // next char decides kind of record
    const char c = *from++;
    //qDebug() << "CODE:" << c;
    switch (c) {
        case '*':
        case '+':
        case '=': {
            QByteArray asyncClass;
            for (; from != to; ++from) {
                const char c = *from;
                if (!isNameChar(c))
                    break;
                asyncClass += *from;
            }
            //qDebug() << "ASYNCCLASS" << asyncClass;
con's avatar
con committed
370

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

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

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

461 462 463 464 465 466
        case '&': {
            QByteArray data = GdbMi::parseCString(from, to);
            m_pendingLogStreamOutput += data;
            // On Windows, the contents seem to depend on the debugger
            // version and/or OS version used.
            if (data.startsWith("warning:"))
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
467
                qq->showApplicationOutput(_(data.mid(9))); // cut "warning: "
con's avatar
con committed
468 469 470
            break;
        }

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

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

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

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
480
            QByteArray resultClass = QByteArray::fromRawData(from, inner - from);
481 482 483 484 485 486 487 488 489 490 491 492
            if (resultClass == "done")
                record.resultClass = GdbResultDone;
            else if (resultClass == "running")
                record.resultClass = GdbResultRunning;
            else if (resultClass == "connected")
                record.resultClass = GdbResultConnected;
            else if (resultClass == "error")
                record.resultClass = GdbResultError;
            else if (resultClass == "exit")
                record.resultClass = GdbResultExit;
            else
                record.resultClass = GdbResultUnknown;
con's avatar
con committed
493

494 495
            from = inner;
            if (from != to) {
hjk's avatar
hjk committed
496 497 498 499 500 501 502 503 504
                if (*from == ',') {
                    ++from;
                    record.data.parseTuple_helper(from, to);
                    record.data.m_type = GdbMi::Tuple;
                    record.data.m_name = "data";
                } else {
                    // Archer has this
                    record.data.m_type = GdbMi::Tuple;
                    record.data.m_name = "data";
con's avatar
con committed
505 506
                }
            }
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

            //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
            //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
            //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
            record.data.setStreamOutput("logstreamoutput",
                m_pendingLogStreamOutput);
            record.data.setStreamOutput("targetstreamoutput",
                m_pendingTargetStreamOutput);
            record.data.setStreamOutput("consolestreamoutput",
                m_pendingConsoleStreamOutput);
            QByteArray custom = m_customOutputForToken[token];
            if (!custom.isEmpty())
                record.data.setStreamOutput("customvaluecontents",
                    '{' + custom + '}');
            //m_customOutputForToken.remove(token);
            m_pendingLogStreamOutput.clear();
            m_pendingTargetStreamOutput.clear();
            m_pendingConsoleStreamOutput.clear();

            handleResultRecord(record);
            break;
        }
        default: {
            qDebug() << "UNKNOWN RESPONSE TYPE" << c;
            break;
con's avatar
con committed
532 533 534 535
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // This is somewhat inefficient, as it makes the last command synchronous.
    // An optimization would be requesting the continue immediately when the
    // event loop is entered, and let individual commands have a flag to suppress
    // that behavior.
    if (m_cookieForToken.isEmpty() && m_autoContinue) {
        m_autoContinue = false;
        continueInferior();
        q->showStatusMessage(tr("Continuing after temporary stop."));
    }
con's avatar
con committed
742 743 744 745 746
}

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

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

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

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

795
void GdbEngine::handleInfoThreads(const GdbResultRecord &record, const QVariant &)
796
{
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
    if (record.resultClass != GdbResultDone)
        return;
    // FIXME: use something more robust
    // WIN:     [New thread 3380.0x2bc]
    //          * 3 Thread 2312.0x4d0  0x7c91120f in ?? ()
    // LINUX:   * 1 Thread 0x7f466273c6f0 (LWP 21455)  0x0000000000404542 in ...
    const QString data = _(record.data.findChild("consolestreamoutput").data());
    if (data.isEmpty())
        return;
    // check "[New thread 3380.0x2bc]"
    if (data.startsWith(QLatin1Char('['))) {
        QRegExp ren(_("^\\[New thread (\\d+)\\.0x.*"));
        Q_ASSERT(ren.isValid());
        if (ren.indexIn(data) != -1) {
            maybeHandleInferiorPidChanged(ren.cap(1));
            return;
        }
814
    }
815 816 817 818 819
    // check "* 3 Thread ..."
    QRegExp re(_("^\\*? +\\d+ +[Tt]hread (\\d+)\\.0x.* in"));
    Q_ASSERT(re.isValid());
    if (re.indexIn(data) != -1)
        maybeHandleInferiorPidChanged(re.cap(1));
820 821
}

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

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

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

848
#if 0
con's avatar
con committed
849 850 851 852 853 854 855 856 857 858
void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
{
    // FIXME: remove this special case as soon as 'jump'
    // is supported by MI
    // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
    // ~"Continuing at 0x4058f3."
    // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
    // ~"242\t x *= 2;"
    //109^done"
    qq->notifyInferiorStopped();
859
    q->showStatusMessage(tr("Jumped. Stopped."));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
860
    QByteArray output = record.data.findChild("logstreamoutput").data();
861
    if (output.isEmpty())
con's avatar
con committed
862
        return;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
863 864 865 866 867 868 869 870 871
    int idx1 = output.indexOf(' ') + 1;
    if (idx1 > 0) {
        int idx2 = output.indexOf(':', idx1);
        if (idx2 > 0) {
            QString file = QString::fromLocal8Bit(output.mid(idx1, idx2 - idx1));
            int line = output.mid(idx2 + 1).toInt();
            q->gotoLocation(file, line, true);
        }
    }
con's avatar
con committed
872
}
873
#endif
con's avatar
con committed
874

875
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record, const QVariant &)
con's avatar
con committed
876 877 878 879 880 881 882 883
{
    // FIXME: remove this special case as soon as there's a real
    // reason given when the temporary breakpoint is hit.
    // reight now we get:
    // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
    // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
    // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
    qq->notifyInferiorStopped();
884
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
con's avatar
con committed
885
    GdbMi frame = record.data.findChild("frame");
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
886
    QString file = QString::fromLocal8Bit(frame.findChild("fullname").data());
con's avatar
con committed
887
    int line = frame.findChild("line").data().toInt();
888 889
    qDebug() << "HIT:" << file << line << "IN" << frame.toString()
        << "--" << record.toString();
con's avatar
con committed
890 891 892
    q->gotoLocation(file, line, true);
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
893
static bool isExitedReason(const QByteArray &reason)
con's avatar
con committed
894
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
895 896 897 898
    return reason == "exited-normally"   // inferior exited normally
        || reason == "exited-signalled"  // inferior exited because of a signal
        //|| reason == "signal-received" // inferior received signal
        || reason == "exited";           // inferior exited
con's avatar
con committed
899 900
}

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
901
static bool isStoppedReason(const QByteArray &reason)
con's avatar
con committed
902
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
903 904 905 906 907 908 909
    return reason == "function-finished"  // -exec-finish
        || reason == "signal-received"  // handled as "isExitedReason"
        || reason == "breakpoint-hit"     // -exec-continue
        || reason == "end-stepping-range" // -exec-next, -exec-step
        || reason == "location-reached"   // -exec-until
        || reason == "access-watchpoint-trigger"
        || reason == "read-watchpoint-trigger"
910
        #ifdef Q_OS_MAC
con's avatar
con committed
911
        || reason.isEmpty()
912
        #endif
con's avatar
con committed
913 914 915
    ;
}

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

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

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

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

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

961 962
    if (isExitedReason(reason)) {
        qq->notifyInferiorExited();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
963
        QString msg;
964
        if (reason == "exited") {
965 966 967 968 969
            msg = tr("Program exited with exit code %1")
                .arg(_(data.findChild("exit-code").toString()));
        } else if (reason == "exited-signalled" || reason == "signal-received") {
            msg = tr("Program exited after receiving signal %1")
                .arg(_(data.findChild("signal-name").toString()));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
970
        } else {
971
            msg = tr("Program exited normally");
972 973
        }
        q->showStatusMessage(msg);
974
        postCommand(_("-gdb-exit"), CB(handleExit));
975 976 977 978
        return;
    }


979 980 981
    //MAC: bool isFirstStop = data.findChild("bkptno").data() == "1";
    //!MAC: startSymbolName == data.findChild("frame").findChild("func")
    if (m_waitingForFirstBreakpointToBeHit) {
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
982 983
        m_waitingForFirstBreakpointToBeHit = false;

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

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

hjk's avatar
hjk committed
994
    if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
Roberto Raggi's avatar
Roberto Raggi committed
995
        QTC_ASSERT(q->status() == DebuggerInferiorStopRequested,
996
            qDebug() << "STATUS:" << q->status())