forked from qt-creator/qt-creator
		
	
		
			
				
	
	
		
			4020 lines
		
	
	
		
			134 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4020 lines
		
	
	
		
			134 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/***************************************************************************
 | 
						|
**
 | 
						|
** This file is part of Qt Creator
 | 
						|
**
 | 
						|
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
 | 
						|
**
 | 
						|
** Contact:  Qt Software Information (qt-info@nokia.com)
 | 
						|
**
 | 
						|
**
 | 
						|
** Non-Open Source Usage
 | 
						|
**
 | 
						|
** Licensees may use this file in accordance with the Qt Beta Version
 | 
						|
** License Agreement, Agreement version 2.2 provided with the Software or,
 | 
						|
** alternatively, in accordance with the terms contained in a written
 | 
						|
** agreement between you and Nokia.
 | 
						|
**
 | 
						|
** GNU General Public License Usage
 | 
						|
**
 | 
						|
** Alternatively, this file may be used under the terms of the GNU General
 | 
						|
** Public License versions 2.0 or 3.0 as published by the Free Software
 | 
						|
** Foundation and appearing in the file LICENSE.GPL included in the packaging
 | 
						|
** of this file.  Please review the following information to ensure GNU
 | 
						|
** General Public Licensing requirements will be met:
 | 
						|
**
 | 
						|
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
 | 
						|
** http://www.gnu.org/copyleft/gpl.html.
 | 
						|
**
 | 
						|
** In addition, as a special exception, Nokia gives you certain additional
 | 
						|
** rights. These rights are described in the Nokia Qt GPL Exception
 | 
						|
** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
 | 
						|
**
 | 
						|
***************************************************************************/
 | 
						|
 | 
						|
#include "gdbengine.h"
 | 
						|
 | 
						|
#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"
 | 
						|
 | 
						|
#include "startexternaldialog.h"
 | 
						|
#include "attachexternaldialog.h"
 | 
						|
 | 
						|
#include <utils/qtcassert.h>
 | 
						|
 | 
						|
#include <QtCore/QDebug>
 | 
						|
#include <QtCore/QDir>
 | 
						|
#include <QtCore/QFileInfo>
 | 
						|
#include <QtCore/QTime>
 | 
						|
#include <QtCore/QTimer>
 | 
						|
 | 
						|
#include <QtGui/QAction>
 | 
						|
#include <QtGui/QLabel>
 | 
						|
#include <QtGui/QMainWindow>
 | 
						|
#include <QtGui/QMessageBox>
 | 
						|
#include <QtGui/QToolTip>
 | 
						|
 | 
						|
#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
 | 
						|
#include <unistd.h>
 | 
						|
#include <dlfcn.h>
 | 
						|
#endif
 | 
						|
 | 
						|
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
 | 
						|
#   define PENDING_DEBUG(s) 
 | 
						|
#endif
 | 
						|
 | 
						|
static const QString tooltipIName = "tooltip";
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// GdbCommandType
 | 
						|
//
 | 
						|
///////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
enum GdbCommandType
 | 
						|
{
 | 
						|
    GdbInvalidCommand = 0,
 | 
						|
 | 
						|
    GdbShowVersion = 100,
 | 
						|
    GdbFileExecAndSymbols,
 | 
						|
    GdbQueryPwd,
 | 
						|
    GdbQuerySources,
 | 
						|
    GdbAsyncOutput2,
 | 
						|
    GdbExecRun,
 | 
						|
    GdbExecRunToFunction,
 | 
						|
    GdbExecStep,
 | 
						|
    GdbExecNext,
 | 
						|
    GdbExecStepI,
 | 
						|
    GdbExecNextI,
 | 
						|
    GdbExecContinue,
 | 
						|
    GdbExecFinish,
 | 
						|
    GdbExecJumpToLine,
 | 
						|
    GdbExecInterrupt,
 | 
						|
    GdbInfoShared,
 | 
						|
    GdbInfoProc,
 | 
						|
    GdbQueryDataDumper1,
 | 
						|
    GdbQueryDataDumper2,
 | 
						|
 | 
						|
    BreakCondition = 200,
 | 
						|
    BreakEnablePending,
 | 
						|
    BreakSetAnnotate,
 | 
						|
    BreakDelete,
 | 
						|
    BreakList,
 | 
						|
    BreakIgnore,
 | 
						|
    BreakInfo,
 | 
						|
    BreakInsert,
 | 
						|
    BreakInsert1,
 | 
						|
 | 
						|
    DisassemblerList = 300,
 | 
						|
 | 
						|
    ModulesList = 400,
 | 
						|
 | 
						|
    RegisterListNames = 500,
 | 
						|
    RegisterListValues,
 | 
						|
 | 
						|
    StackSelectThread = 600,
 | 
						|
    StackListThreads,
 | 
						|
    StackListFrames,
 | 
						|
    StackListLocals,
 | 
						|
    StackListArguments,
 | 
						|
 | 
						|
    WatchVarAssign = 700,             // data changed by user
 | 
						|
    WatchVarListChildren,
 | 
						|
    WatchVarCreate,
 | 
						|
    WatchEvaluateExpression,
 | 
						|
    WatchToolTip,
 | 
						|
    WatchDumpCustomSetup,
 | 
						|
    WatchDumpCustomValue1,           // waiting for gdb ack
 | 
						|
    WatchDumpCustomValue2,           // waiting for actual data
 | 
						|
    WatchDumpCustomEditValue,
 | 
						|
};
 | 
						|
 | 
						|
QString dotEscape(QString str)
 | 
						|
{
 | 
						|
    str.replace(' ', '.');
 | 
						|
    str.replace('\\', '.');
 | 
						|
    str.replace('/', '.');
 | 
						|
    return str;
 | 
						|
}
 | 
						|
 | 
						|
QString currentTime()
 | 
						|
{
 | 
						|
    return QTime::currentTime().toString("hh:mm:ss.zzz");
 | 
						|
}
 | 
						|
 | 
						|
static int ¤tToken()
 | 
						|
{
 | 
						|
    static int token = 0;
 | 
						|
    return token;
 | 
						|
}
 | 
						|
 | 
						|
static bool isSkippableFunction(const QString &funcName, const QString &fileName)
 | 
						|
{
 | 
						|
    if (fileName.endsWith("kernel/qobject.cpp"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("kernel/moc_qobject.cpp"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("kernel/qmetaobject.cpp"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith(".moc"))
 | 
						|
        return true;
 | 
						|
 | 
						|
    if (funcName.endsWith("::qt_metacall"))
 | 
						|
        return true;
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool isLeavableFunction(const QString &funcName, const QString &fileName)
 | 
						|
{
 | 
						|
    if (funcName.endsWith("QObjectPrivate::setCurrentSender"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("kernel/qmetaobject.cpp")
 | 
						|
            && funcName.endsWith("QMetaObject::methodOffset"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("kernel/qobject.h"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("kernel/qobject.cpp")
 | 
						|
            && funcName.endsWith("QObjectConnectionListVector::at"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("kernel/qobject.cpp")
 | 
						|
            && funcName.endsWith("~QObject"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("thread/qmutex.cpp"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("thread/qthread.cpp"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("thread/qthread_unix.cpp"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("thread/qmutex.h"))
 | 
						|
        return true;
 | 
						|
    if (fileName.contains("thread/qbasicatomic"))
 | 
						|
        return true;
 | 
						|
    if (fileName.contains("thread/qorderedmutexlocker_p"))
 | 
						|
        return true;
 | 
						|
    if (fileName.contains("arch/qatomic"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("tools/qvector.h"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("tools/qlist.h"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("tools/qhash.h"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("tools/qmap.h"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("tools/qstring.h"))
 | 
						|
        return true;
 | 
						|
    if (fileName.endsWith("global/qglobal.h"))
 | 
						|
        return true;
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// GdbEngine
 | 
						|
//
 | 
						|
///////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
GdbEngine::GdbEngine(DebuggerManager *parent)
 | 
						|
{
 | 
						|
    q = parent;
 | 
						|
    qq = parent->engineInterface();
 | 
						|
    init();
 | 
						|
}
 | 
						|
 | 
						|
GdbEngine::~GdbEngine()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::init()
 | 
						|
{
 | 
						|
    m_pendingRequests = 0;
 | 
						|
    m_gdbVersion = 100;
 | 
						|
    m_shared = 0;
 | 
						|
    m_outputCodec = QTextCodec::codecForLocale();
 | 
						|
    m_dataDumperState = DataDumperUninitialized;
 | 
						|
 | 
						|
    m_oldestAcceptableToken = -1;
 | 
						|
 | 
						|
    // Gdb Process interaction
 | 
						|
    connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this,
 | 
						|
        SLOT(gdbProcError(QProcess::ProcessError)));
 | 
						|
    connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this,
 | 
						|
        SLOT(readGdbStandardOutput()));
 | 
						|
    connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this,
 | 
						|
        SLOT(readGdbStandardError()));
 | 
						|
    connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q,
 | 
						|
        SLOT(exitDebugger()));
 | 
						|
 | 
						|
    // Output
 | 
						|
    connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
 | 
						|
            SLOT(readDebugeeOutput(QByteArray)));
 | 
						|
    connect(this, SIGNAL(gdbResponseAvailable()),
 | 
						|
        this, SLOT(handleResponse()), Qt::QueuedConnection);
 | 
						|
 | 
						|
    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);
 | 
						|
    connect(this, SIGNAL(applicationOutputAvailable(QString)),
 | 
						|
        q, SLOT(showApplicationOutput(QString)),
 | 
						|
        Qt::QueuedConnection);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::gdbProcError(QProcess::ProcessError error)
 | 
						|
{
 | 
						|
    QString msg;
 | 
						|
    switch (error) {
 | 
						|
        case QProcess::FailedToStart:
 | 
						|
            msg = QString(tr("The Gdb process failed to start. Either the "
 | 
						|
                "invoked program '%1' is missing, or you may have insufficient "
 | 
						|
                "permissions to invoke the program.")).arg(q->settings()->m_gdbCmd);
 | 
						|
            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().");
 | 
						|
    }
 | 
						|
 | 
						|
    q->showStatusMessage(msg);
 | 
						|
    QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
 | 
						|
    // act as if it was closed by the core
 | 
						|
    q->exitDebugger();
 | 
						|
}
 | 
						|
 | 
						|
static void skipSpaces(const char *&from, const char *to)
 | 
						|
{
 | 
						|
    while (from != to && QChar(*from).isSpace())
 | 
						|
        ++from;
 | 
						|
}
 | 
						|
 | 
						|
static inline bool isNameChar(char c)
 | 
						|
{
 | 
						|
    // could be 'stopped' or 'shlibs-added'
 | 
						|
    return (c >= 'a' && c <= 'z') || c == '-';
 | 
						|
}
 | 
						|
 | 
						|
#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
 | 
						|
 | 
						|
static void skipTerminator(const char *&from, const char *to)
 | 
						|
{
 | 
						|
    skipSpaces(from, to);
 | 
						|
    // skip '(gdb)'
 | 
						|
    if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')')
 | 
						|
        from += 5;
 | 
						|
    skipSpaces(from, to);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::readDebugeeOutput(const QByteArray &data)
 | 
						|
{
 | 
						|
    emit applicationOutputAvailable(m_outputCodec->toUnicode(
 | 
						|
            data.constData(), data.length(), &m_outputCodecState));
 | 
						|
}
 | 
						|
 | 
						|
// called asyncronously as response to Gdb stdout output in
 | 
						|
// gdbResponseAvailable()
 | 
						|
void GdbEngine::handleResponse()
 | 
						|
{
 | 
						|
    static QTime lastTime;
 | 
						|
 | 
						|
    emit gdbOutputAvailable("            ", currentTime());
 | 
						|
    emit gdbOutputAvailable("stdout:", m_inbuffer);
 | 
						|
 | 
						|
#if 0
 | 
						|
    qDebug() // << "#### start response handling #### "
 | 
						|
        << currentTime()
 | 
						|
        << lastTime.msecsTo(QTime::currentTime()) << "ms,"
 | 
						|
        << "buf: " << m_inbuffer.left(1500) << "..."
 | 
						|
        //<< "buf: " << m_inbuffer
 | 
						|
        << "size:" << m_inbuffer.size();
 | 
						|
#else
 | 
						|
    //qDebug() << "buf: " << m_inbuffer;
 | 
						|
#endif
 | 
						|
 | 
						|
    lastTime = QTime::currentTime();
 | 
						|
 | 
						|
    while (1) {
 | 
						|
        if (m_inbuffer.isEmpty())
 | 
						|
            break;
 | 
						|
 | 
						|
        const char *from = m_inbuffer.constData();
 | 
						|
        // FIXME: check for line ending in '\n(gdb)\n'
 | 
						|
        const char *to = from + m_inbuffer.size();
 | 
						|
        const char *inner;
 | 
						|
 | 
						|
        //const char *oldfrom = from;
 | 
						|
 | 
						|
        //skipSpaces(from, to);
 | 
						|
        skipTerminator(from, to);
 | 
						|
        int token = -1;
 | 
						|
 | 
						|
        // token is a sequence of numbers
 | 
						|
        for (inner = from; inner != to; ++inner)
 | 
						|
            if (*inner < '0' || *inner > '9')
 | 
						|
                break;
 | 
						|
        if (from != inner) {
 | 
						|
            token = QString(QByteArray(from, inner - from)).toInt();
 | 
						|
            from = inner;
 | 
						|
            //qDebug() << "found token " << token;
 | 
						|
        }
 | 
						|
 | 
						|
        if (from == to) {
 | 
						|
            //qDebug() << "Returning: " << toString();
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        // 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;
 | 
						|
 | 
						|
                GdbMi record;
 | 
						|
                while (from != to && *from == ',') {
 | 
						|
                    ++from; // skip ','
 | 
						|
                    GdbMi data;
 | 
						|
                    data.parseResultOrValue(from, to);
 | 
						|
                    if (data.isValid()) {
 | 
						|
                        //qDebug() << "parsed response: " << data.toString();
 | 
						|
                        record.m_children += data;
 | 
						|
                        record.m_type = GdbMi::Tuple;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                //dump(oldfrom, from, record.toString());
 | 
						|
                skipTerminator(from, to);
 | 
						|
                m_inbuffer = QByteArray(from, to - from);
 | 
						|
                if (asyncClass == "stopped") {
 | 
						|
                    handleAsyncOutput(record);
 | 
						|
                } else if (asyncClass == "running") {
 | 
						|
                    // Archer has 'thread-id="all"' here
 | 
						|
                } else {
 | 
						|
                    qDebug() << "IGNORED ASYNC OUTPUT " << record.toString();
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case '~':
 | 
						|
            case '@':
 | 
						|
            case '&': {
 | 
						|
                QString data = GdbMi::parseCString(from, to);
 | 
						|
                handleStreamOutput(data, c);
 | 
						|
                //dump(oldfrom, from, record.toString());
 | 
						|
                m_inbuffer = QByteArray(from, to - from);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case '^': {
 | 
						|
                GdbResultRecord record;
 | 
						|
 | 
						|
                record.token = token;
 | 
						|
 | 
						|
                for (inner = from; inner != to; ++inner)
 | 
						|
                    if (*inner < 'a' || *inner > 'z')
 | 
						|
                        break;
 | 
						|
 | 
						|
                QByteArray resultClass(from, inner - from);
 | 
						|
 | 
						|
                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;
 | 
						|
 | 
						|
                from = inner;
 | 
						|
                skipSpaces(from, to);
 | 
						|
                if (from != to && *from == ',') {
 | 
						|
                    ++from;
 | 
						|
                    record.data.parseTuple_helper(from, to);
 | 
						|
                    record.data.m_type = GdbMi::Tuple;
 | 
						|
                    record.data.m_name = "data";
 | 
						|
                }
 | 
						|
                skipSpaces(from, to);
 | 
						|
                skipTerminator(from, to);
 | 
						|
 | 
						|
                //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();
 | 
						|
 | 
						|
                //dump(oldfrom, from, record.toString());
 | 
						|
                m_inbuffer = QByteArray(from, to - from);
 | 
						|
                handleResultRecord(record);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            default: {
 | 
						|
                qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer;
 | 
						|
                m_inbuffer = QByteArray(from, to - from);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    //qDebug() << "##### end response handling ####\n\n\n"
 | 
						|
    //    << currentTime() << lastTime.msecsTo(QTime::currentTime());
 | 
						|
    lastTime = QTime::currentTime();
 | 
						|
}
 | 
						|
 | 
						|
#ifdef Q_OS_MAC
 | 
						|
static void fixMac(QByteArray &out)
 | 
						|
{
 | 
						|
    // HACK: gdb on Mac mixes MI1 and MI2 syntax. Not nice.
 | 
						|
    // it returns:   9^done,locals={{name="a"},{name="w"}}
 | 
						|
    // instead of:   9^done,locals=[{name="a"},{name="w"}]
 | 
						|
    if (!out.contains("locals={{name"))
 | 
						|
        return;
 | 
						|
 | 
						|
    static const QByteArray termArray("(gdb) ");
 | 
						|
    int pos = out.indexOf(termArray);
 | 
						|
    if (pos == -1)
 | 
						|
        return;
 | 
						|
 | 
						|
    int pos1 = out.indexOf("={{");
 | 
						|
    if (pos1 == -1)
 | 
						|
        return;
 | 
						|
 | 
						|
    int pos2 = out.indexOf("]]");
 | 
						|
    if (pos2 == -1)
 | 
						|
        return;
 | 
						|
 | 
						|
    if (pos1 < pos && pos2 < pos) {
 | 
						|
        out[pos1 + 1] = '[';
 | 
						|
        out[pos2 + 1] = ']';
 | 
						|
    }
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
void GdbEngine::readGdbStandardError()
 | 
						|
{
 | 
						|
    qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::readGdbStandardOutput()
 | 
						|
{
 | 
						|
    // This is the function called whenever the Gdb process created
 | 
						|
    // output. As a rule of thumb, stdout contains _real_ Gdb output
 | 
						|
    // as responses to our command
 | 
						|
    // and "spontaneous" events like messages on loaded shared libraries.
 | 
						|
    // OTOH, stderr contains application output produced by qDebug etc.
 | 
						|
    // There is no organized way to pass application stdout output.
 | 
						|
 | 
						|
    QByteArray out = m_gdbProc.readAllStandardOutput();
 | 
						|
 | 
						|
    //qDebug() << "\n\n\nPLUGIN OUT: '" <<  out.data() << "'\n\n\n";
 | 
						|
 | 
						|
    #ifdef Q_OS_MAC
 | 
						|
    fixMac(out);
 | 
						|
    #endif
 | 
						|
 | 
						|
    m_inbuffer.append(out);
 | 
						|
    //QTC_ASSERT(!m_inbuffer.isEmpty(), return);
 | 
						|
 | 
						|
    char c = m_inbuffer[m_inbuffer.size() - 1];
 | 
						|
    static const QByteArray termArray("(gdb) ");
 | 
						|
    if (out.indexOf(termArray) == -1 && c != 10 && c != 13) {
 | 
						|
        //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive";
 | 
						|
        //qDebug() << m_inbuffer.data() << m_inbuffer.size();
 | 
						|
        //qDebug() << "\n\n";
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    emit gdbResponseAvailable();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::interruptInferior()
 | 
						|
{
 | 
						|
    if (m_gdbProc.state() == QProcess::NotRunning)
 | 
						|
        return;
 | 
						|
 | 
						|
    if (q->m_attachedPID > 0) {
 | 
						|
        if (interruptProcess(q->m_attachedPID))
 | 
						|
            qq->notifyInferiorStopped();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef Q_OS_MAC
 | 
						|
    sendCommand("-exec-interrupt", GdbExecInterrupt);
 | 
						|
    qq->notifyInferiorStopped();
 | 
						|
#else
 | 
						|
    qDebug() << "CANNOT STOP INFERIOR" << m_gdbProc.pid();
 | 
						|
    if (interruptChildProcess(m_gdbProc.pid()))
 | 
						|
        qq->notifyInferiorStopped();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
 | 
						|
{
 | 
						|
    int pid = pid0.toInt();
 | 
						|
    if (pid == 0) {
 | 
						|
        qDebug() << "Cannot parse PID from " << pid0;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    if (pid == q->m_attachedPID)
 | 
						|
        return;
 | 
						|
    q->m_attachedPID = pid;
 | 
						|
    qq->notifyInferiorPidChanged(pid); 
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::sendSynchronizedCommand(const QString & command,
 | 
						|
    int type, const QVariant &cookie, bool needStop)
 | 
						|
{
 | 
						|
    sendCommand(command, type, cookie, needStop, true);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::sendCommand(const QString &command, int type,
 | 
						|
    const QVariant &cookie, bool needStop, bool synchronized)
 | 
						|
{
 | 
						|
    if (m_gdbProc.state() == QProcess::NotRunning) {
 | 
						|
        //qDebug() << "NO GDB PROCESS RUNNING, CMD IGNORED:" << command;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    bool temporarilyStopped = false;
 | 
						|
    if (needStop && q->status() == DebuggerInferiorRunning) {
 | 
						|
        q->showStatusMessage(tr("Temporarily stopped"));
 | 
						|
        interruptInferior();
 | 
						|
        temporarilyStopped = true;
 | 
						|
    }
 | 
						|
 | 
						|
    ++currentToken();
 | 
						|
    if (synchronized) {
 | 
						|
        ++m_pendingRequests;
 | 
						|
        PENDING_DEBUG("   TYPE " << type << " INCREMENTS PENDING TO: "
 | 
						|
            << m_pendingRequests << command);
 | 
						|
    } else {
 | 
						|
        PENDING_DEBUG("   UNKNOWN TYPE " << type << " LEAVES PENDING AT: "
 | 
						|
            << m_pendingRequests << command);
 | 
						|
    }
 | 
						|
 | 
						|
    GdbCookie cmd;
 | 
						|
    cmd.synchronized = synchronized;
 | 
						|
    cmd.command = command;
 | 
						|
    cmd.command = QString::number(currentToken()) + cmd.command;
 | 
						|
    if (cmd.command.contains("%1"))
 | 
						|
        cmd.command = cmd.command.arg(currentToken());
 | 
						|
    cmd.type = type;
 | 
						|
    cmd.cookie = cookie;
 | 
						|
 | 
						|
    m_cookieForToken[currentToken()] = cmd;
 | 
						|
 | 
						|
    //qDebug() << "";
 | 
						|
    if (!command.isEmpty()) {
 | 
						|
        //qDebug() << qPrintable(currentTime()) << "RUNNING" << cmd.command;
 | 
						|
        m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
 | 
						|
        //emit gdbInputAvailable(QString(), "         " +  currentTime());
 | 
						|
        emit gdbInputAvailable(QString(), "[" + currentTime() + "]    " + cmd.command);
 | 
						|
        //emit gdbInputAvailable(QString(), cmd.command);
 | 
						|
    }
 | 
						|
 | 
						|
    if (temporarilyStopped)
 | 
						|
        sendCommand("-exec-continue");
 | 
						|
 | 
						|
    // slows down
 | 
						|
    //qApp->processEvents();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleResultRecord(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    //qDebug() << "TOKEN: " << record.token
 | 
						|
    //    << " ACCEPTABLE: " << m_oldestAcceptableToken;
 | 
						|
    //qDebug() << "";
 | 
						|
    //qDebug() << "\nRESULT" << record.token << record.toString();
 | 
						|
 | 
						|
    int token = record.token;
 | 
						|
    if (token == -1)
 | 
						|
        return;
 | 
						|
 | 
						|
    GdbCookie cmd = m_cookieForToken.take(token);
 | 
						|
 | 
						|
    // FIXME: this falsely rejects results from the custom dumper recognition
 | 
						|
    // procedure, too...
 | 
						|
    if (record.token < m_oldestAcceptableToken) {
 | 
						|
        //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
 | 
						|
        //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
#if 0
 | 
						|
    qDebug() << "# handleOutput, "
 | 
						|
        << "cmd type: " << cmd.type
 | 
						|
        << "cmd synchronized: " << cmd.synchronized
 | 
						|
        << "\n record: " << record.toString();
 | 
						|
#endif
 | 
						|
 | 
						|
    // << "\n data: " << record.data.toString(true);
 | 
						|
 | 
						|
    if (cmd.type != GdbInvalidCommand)
 | 
						|
        handleResult(record, cmd.type, cmd.cookie);
 | 
						|
 | 
						|
    if (cmd.synchronized) {
 | 
						|
        --m_pendingRequests;
 | 
						|
        PENDING_DEBUG("   TYPE " << cmd.type << " DECREMENTS PENDING TO: "
 | 
						|
            << m_pendingRequests << cmd.command);
 | 
						|
        if (m_pendingRequests <= 0)
 | 
						|
            updateWatchModel2();
 | 
						|
    } else {
 | 
						|
        PENDING_DEBUG("   UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
 | 
						|
            << m_pendingRequests << cmd.command);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleResult(const GdbResultRecord & record, int type,
 | 
						|
    const QVariant & cookie)
 | 
						|
{
 | 
						|
    switch (type) {
 | 
						|
        case GdbExecNext:
 | 
						|
        case GdbExecStep:
 | 
						|
        case GdbExecNextI:
 | 
						|
        case GdbExecStepI:
 | 
						|
        case GdbExecContinue:
 | 
						|
        case GdbExecFinish:
 | 
						|
            // evil code sharing
 | 
						|
        case GdbExecRun:
 | 
						|
            handleExecRun(record);
 | 
						|
            break;
 | 
						|
        case GdbInfoProc:
 | 
						|
            handleInfoProc(record);
 | 
						|
            break;
 | 
						|
 | 
						|
        case GdbShowVersion:
 | 
						|
            handleShowVersion(record);
 | 
						|
            break;
 | 
						|
        case GdbFileExecAndSymbols:
 | 
						|
            handleFileExecAndSymbols(record);
 | 
						|
            break;
 | 
						|
        case GdbExecRunToFunction:
 | 
						|
            // that should be "^running". We need to handle the resulting
 | 
						|
            // "Stopped"
 | 
						|
            //handleExecRunToFunction(record);
 | 
						|
            break;
 | 
						|
        case GdbExecInterrupt:
 | 
						|
            break;
 | 
						|
        case GdbExecJumpToLine:
 | 
						|
            handleExecJumpToLine(record);
 | 
						|
            break;
 | 
						|
        case GdbQueryPwd:
 | 
						|
            handleQueryPwd(record);
 | 
						|
            break;
 | 
						|
        case GdbQuerySources:
 | 
						|
            handleQuerySources(record);
 | 
						|
            break;
 | 
						|
        case GdbAsyncOutput2:
 | 
						|
            handleAsyncOutput2(cookie.value<GdbMi>());
 | 
						|
            break;
 | 
						|
        case GdbInfoShared:
 | 
						|
            handleInfoShared(record);
 | 
						|
            break;
 | 
						|
        case GdbQueryDataDumper1:
 | 
						|
            handleQueryDataDumper1(record);
 | 
						|
            break;
 | 
						|
        case GdbQueryDataDumper2:
 | 
						|
            handleQueryDataDumper2(record);
 | 
						|
            break;
 | 
						|
 | 
						|
        case BreakList:
 | 
						|
            handleBreakList(record);
 | 
						|
            break;
 | 
						|
        case BreakInsert:
 | 
						|
            handleBreakInsert(record, cookie.toInt());
 | 
						|
            break;
 | 
						|
        case BreakInsert1:
 | 
						|
            handleBreakInsert1(record, cookie.toInt());
 | 
						|
            break;
 | 
						|
        case BreakInfo:
 | 
						|
            handleBreakInfo(record, cookie.toInt());
 | 
						|
            break;
 | 
						|
        case BreakEnablePending:
 | 
						|
        case BreakDelete:
 | 
						|
            // nothing
 | 
						|
            break;
 | 
						|
        case BreakIgnore:
 | 
						|
            handleBreakIgnore(record, cookie.toInt());
 | 
						|
            break;
 | 
						|
        case BreakCondition:
 | 
						|
            handleBreakCondition(record, cookie.toInt());
 | 
						|
            break;
 | 
						|
 | 
						|
        case DisassemblerList:
 | 
						|
            handleDisassemblerList(record, cookie.toString());
 | 
						|
            break;
 | 
						|
 | 
						|
        case ModulesList:
 | 
						|
            handleModulesList(record);
 | 
						|
            break;
 | 
						|
 | 
						|
        case RegisterListNames:
 | 
						|
            handleRegisterListNames(record);
 | 
						|
            break;
 | 
						|
        case RegisterListValues:
 | 
						|
            handleRegisterListValues(record);
 | 
						|
            break;
 | 
						|
 | 
						|
        case StackListFrames:
 | 
						|
            handleStackListFrames(record);
 | 
						|
            break;
 | 
						|
        case StackListThreads:
 | 
						|
            handleStackListThreads(record, cookie.toInt());
 | 
						|
            break;
 | 
						|
        case StackSelectThread:
 | 
						|
            handleStackSelectThread(record, cookie.toInt());
 | 
						|
            break;
 | 
						|
        case StackListLocals:
 | 
						|
            handleStackListLocals(record);
 | 
						|
            break;
 | 
						|
        case StackListArguments:
 | 
						|
            handleStackListArguments(record);
 | 
						|
            break;
 | 
						|
 | 
						|
        case WatchVarListChildren:
 | 
						|
            handleVarListChildren(record, cookie.value<WatchData>());
 | 
						|
            break;
 | 
						|
        case WatchVarCreate:
 | 
						|
            handleVarCreate(record, cookie.value<WatchData>());
 | 
						|
            break;
 | 
						|
        case WatchVarAssign:
 | 
						|
            handleVarAssign();
 | 
						|
            break;
 | 
						|
        case WatchEvaluateExpression:
 | 
						|
            handleEvaluateExpression(record, cookie.value<WatchData>());
 | 
						|
            break;
 | 
						|
        case WatchToolTip:
 | 
						|
            handleToolTip(record, cookie.toString());
 | 
						|
            break;
 | 
						|
        case WatchDumpCustomValue1:
 | 
						|
            handleDumpCustomValue1(record, cookie.value<WatchData>());
 | 
						|
            break;
 | 
						|
        case WatchDumpCustomValue2:
 | 
						|
            handleDumpCustomValue2(record, cookie.value<WatchData>());
 | 
						|
            break;
 | 
						|
        case WatchDumpCustomSetup:
 | 
						|
            handleDumpCustomSetup(record);
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            qDebug() << "FIXME: GdbEngine::handleResult: "
 | 
						|
                "should not happen" << type;
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::executeDebuggerCommand(const QString &command)
 | 
						|
{
 | 
						|
    //createGdbProcessIfNeeded();
 | 
						|
    if (m_gdbProc.state() == QProcess::NotRunning) {
 | 
						|
        qDebug() << "NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " << command;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    GdbCookie cmd;
 | 
						|
    cmd.command = command;
 | 
						|
    cmd.type = -1;
 | 
						|
 | 
						|
    //m_cookieForToken[currentToken()] = cmd;
 | 
						|
    //++currentToken();
 | 
						|
 | 
						|
    //qDebug() << "";
 | 
						|
    //qDebug() << currentTime() << "Running command:   " << cmd.command;
 | 
						|
    emit gdbInputAvailable(QString(), cmd.command);
 | 
						|
    m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    // FIXME: remove this special case as soon as 'pwd'
 | 
						|
    // is supported by MI
 | 
						|
    //qDebug() << "PWD OUTPUT:" <<  record.toString();
 | 
						|
    // Gdb responses _unless_ we get an error first.
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
#ifdef Q_OS_LINUX
 | 
						|
        // "5^done,{logstreamoutput="pwd ",consolestreamoutput
 | 
						|
        // ="Working directory /home/apoenitz/dev/work/test1.  "}
 | 
						|
        m_pwd = record.data.findChild("consolestreamoutput").data();
 | 
						|
        int pos = m_pwd.indexOf("Working directory");
 | 
						|
        m_pwd = m_pwd.mid(pos + 18);
 | 
						|
        m_pwd = m_pwd.trimmed();
 | 
						|
        if (m_pwd.endsWith('.'))
 | 
						|
            m_pwd.chop(1);
 | 
						|
#endif
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
        // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
 | 
						|
        m_pwd = record.data.findChild("consolestreamoutput").data();
 | 
						|
        m_pwd = m_pwd.trimmed();
 | 
						|
#endif
 | 
						|
        //qDebug() << "PWD RESULT:" <<  m_pwd;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleQuerySources(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        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()) {
 | 
						|
            QString fileName = item.findChild("file").data();
 | 
						|
            GdbMi fullName = item.findChild("fullname");
 | 
						|
            QString full = fullName.data();
 | 
						|
            #ifdef Q_OS_WIN
 | 
						|
            full = QDir::cleanPath(full);
 | 
						|
            #endif
 | 
						|
            if (fullName.isValid() && QFileInfo(full).isReadable()) {
 | 
						|
                //qDebug() << "STORING 2: " << fileName << full;
 | 
						|
                m_shortToFullName[fileName] = full;
 | 
						|
                m_fullToShortName[full] = fileName;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleInfoProc(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        #if defined(Q_OS_MAC)
 | 
						|
        //^done,process-id="85075"
 | 
						|
        maybeHandleInferiorPidChanged(record.data.findChild("process-id").data());
 | 
						|
        #endif
 | 
						|
 | 
						|
        #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
 | 
						|
        // FIXME: use something more robust
 | 
						|
        QRegExp re(QLatin1String("process (\\d+)"));
 | 
						|
        QString data = record.data.findChild("consolestreamoutput").data();
 | 
						|
        if (re.indexIn(data) != -1)
 | 
						|
            maybeHandleInferiorPidChanged(re.cap(1));
 | 
						|
        #endif
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleInfoShared(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        // let the modules handler do the parsing
 | 
						|
        handleModulesList(record);
 | 
						|
        QList<Module> modules = qq->modulesHandler()->modules();
 | 
						|
        bool reloadNeeded = false;
 | 
						|
        foreach (const Module &module, modules) {
 | 
						|
            // FIXME: read this from some list
 | 
						|
            if (!module.symbolsRead && !module.moduleName.contains("Q")) {
 | 
						|
                reloadNeeded = true;
 | 
						|
                sendCommand("sharedlibrary " + dotEscape(module.moduleName));
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (reloadNeeded)
 | 
						|
            reloadModules();
 | 
						|
        continueInferior();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
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();
 | 
						|
    q->showStatusMessage(tr("Jumped. Stopped."));
 | 
						|
    QString output = record.data.findChild("logstreamoutput").data();
 | 
						|
    if (!output.isEmpty())
 | 
						|
        return;
 | 
						|
    QString fileAndLine = output.section(' ', 1, 1);
 | 
						|
    QString file = fileAndLine.section(':', 0, 0);
 | 
						|
    int line = fileAndLine.section(':', 1, 1).toInt();
 | 
						|
    q->gotoLocation(file, line, true);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    // 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();
 | 
						|
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
 | 
						|
    GdbMi frame = record.data.findChild("frame");
 | 
						|
    QString file = frame.findChild("fullname").data();
 | 
						|
    int line = frame.findChild("line").data().toInt();
 | 
						|
    qDebug() << "HIT: " << file << line << " IN " << frame.toString()
 | 
						|
        << " -- " << record.toString();
 | 
						|
    q->gotoLocation(file, line, true);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleStreamOutput(const QString &data, char code)
 | 
						|
{
 | 
						|
    // Linux
 | 
						|
    if (data.contains("[New Thread")) {
 | 
						|
        QRegExp re("\\[New Thread 0x([0-9a-f]*) \\(LWP ([0-9]*)\\)\\]");
 | 
						|
        if (re.indexIn(data) != -1)
 | 
						|
            maybeHandleInferiorPidChanged(re.cap(2));
 | 
						|
    }
 | 
						|
 | 
						|
    // Mac
 | 
						|
    if (data.contains("[Switching to process ")) {
 | 
						|
        QRegExp re("\\[Switching to process ([0-9]*) local thread 0x([0-9a-f]*)\\]");
 | 
						|
        if (re.indexIn(data) != -1)
 | 
						|
            maybeHandleInferiorPidChanged(re.cap(1));
 | 
						|
    }
 | 
						|
 | 
						|
    // present it twice: now and together with the next 'real' result
 | 
						|
    switch (code) {
 | 
						|
        case '~':
 | 
						|
            m_pendingConsoleStreamOutput += data;
 | 
						|
            break;
 | 
						|
        case '@':
 | 
						|
            m_pendingTargetStreamOutput += data;
 | 
						|
            break;
 | 
						|
        case '&':
 | 
						|
            m_pendingLogStreamOutput += data;
 | 
						|
            // On Windows, the contents seem to depend on the debugger
 | 
						|
            // version and/or OS version used.
 | 
						|
            if (data.startsWith("warning:"))
 | 
						|
                qq->showApplicationOutput(data);
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef Q_OS_LINUX
 | 
						|
    if (data.startsWith("Pending break") && data.contains("\" resolved")) {
 | 
						|
        qDebug() << "SCHEDULING -break-list";
 | 
						|
        //m_breakListOnStopNeeded = true;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
#if 0
 | 
						|
    if (m_slurpingPTypeOutput)
 | 
						|
        qDebug() << "SLURP: " << output.data;
 | 
						|
 | 
						|
    //  "No symbol \"__dlopen\" in current context."
 | 
						|
    //  "No symbol \"dlopen\" in current context."
 | 
						|
    if (output.data.startsWith("No symbol ")
 | 
						|
            && output.data.contains("dlopen")) {
 | 
						|
        m_dlopened = true;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // output of 'ptype <foo>'
 | 
						|
    if (output.data.startsWith("type = ")) {
 | 
						|
        if (output.data.endsWith("{") || output.data.endsWith("{\\n")) {
 | 
						|
            // multi-line output started here...
 | 
						|
            m_slurpingPTypeOutput = true;
 | 
						|
            m_slurpedPTypeOutput = output.data;
 | 
						|
        } else {
 | 
						|
            // Happens for simple types. Process it immediately
 | 
						|
            m_watchHandler->handleTypeContents(output.data);
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    if (m_slurpingPTypeOutput) {
 | 
						|
        m_slurpedPTypeOutput += '\n';
 | 
						|
        m_slurpedPTypeOutput += output.data;
 | 
						|
        if (output.data.startsWith("}")) {
 | 
						|
            // this is the last line...
 | 
						|
            m_slurpingPTypeOutput = false;
 | 
						|
            m_watchHandler->handleTypeContents(m_slurpedPTypeOutput);
 | 
						|
            m_slurpedPTypeOutput.clear();
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static bool isExitedReason(const QString &reason)
 | 
						|
{
 | 
						|
    return reason == QLatin1String("exited-normally")   // inferior exited normally
 | 
						|
        || reason == QLatin1String("exited-signalled")  // inferior exited because of a signal
 | 
						|
        //|| reason == QLatin1String("signal-received") // inferior received signal
 | 
						|
        || reason == QLatin1String("exited");           // inferior exited
 | 
						|
}
 | 
						|
 | 
						|
static bool isStoppedReason(const QString &reason)
 | 
						|
{
 | 
						|
    return reason == QLatin1String("function-finished")  // -exec-finish
 | 
						|
        || reason == QLatin1String("signal-received")  // handled as "isExitedReason"
 | 
						|
        || reason == QLatin1String("breakpoint-hit")     // -exec-continue
 | 
						|
        || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step
 | 
						|
        || reason == QLatin1String("location-reached")   // -exec-until
 | 
						|
        || reason == QLatin1String("access-watchpoint-trigger")
 | 
						|
        || reason == QLatin1String("read-watchpoint-trigger")
 | 
						|
#ifdef Q_OS_MAC
 | 
						|
        || reason.isEmpty()
 | 
						|
#endif
 | 
						|
    ;
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleAsyncOutput(const GdbMi &data)
 | 
						|
{
 | 
						|
    const QString reason = data.findChild("reason").data();
 | 
						|
 | 
						|
    QString console = data.findChild("consolestreamoutput").data();
 | 
						|
    if (console.contains("Stopped due to shared library event") || reason.isEmpty()) {
 | 
						|
        ++m_shared;
 | 
						|
        //if (m_shared == 2)
 | 
						|
        //    tryLoadCustomDumpers();
 | 
						|
        //qDebug() << "SHARED LIBRARY EVENT " << data.toString() << m_shared;
 | 
						|
        if (qq->useFastStart()) {
 | 
						|
            if (1 || m_shared <= 16) { // libpthread?
 | 
						|
                sendCommand("info shared", GdbInfoShared);
 | 
						|
                //sendCommand("sharedlibrary gdbdebugger ");
 | 
						|
                //continueInferior();
 | 
						|
            } else {
 | 
						|
                // auto-load from now on
 | 
						|
                sendCommand("info shared");
 | 
						|
                sendCommand("set auto-solib-add on");
 | 
						|
                sendCommand("-file-list-exec-source-files", GdbQuerySources);
 | 
						|
                sendCommand("-break-list", BreakList);
 | 
						|
                //sendCommand("bt");
 | 
						|
                //QVariant var = QVariant::fromValue<GdbMi>(data);
 | 
						|
                //sendCommand("p 1", GdbAsyncOutput2, var);  // dummy
 | 
						|
                continueInferior();
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            // slow start requested.
 | 
						|
            q->showStatusMessage(tr("Loading %1...").arg(QString(data.toString())));
 | 
						|
            continueInferior();
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (isExitedReason(reason)) {
 | 
						|
        qq->notifyInferiorExited();
 | 
						|
        QString msg = "Program exited normally";
 | 
						|
        if (reason == "exited") {
 | 
						|
            msg = "Program exited with exit code "
 | 
						|
                + data.findChild("exit-code").toString();
 | 
						|
        } else if (reason == "exited-signalled") {
 | 
						|
            msg = "Program exited after receiving signal "
 | 
						|
                + data.findChild("signal-name").toString();
 | 
						|
        } else if (reason == "signal-received") {
 | 
						|
            msg = "Program exited after receiving signal "
 | 
						|
                + data.findChild("signal-name").toString();
 | 
						|
        }
 | 
						|
        q->showStatusMessage(msg);
 | 
						|
        q->exitDebugger();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    tryLoadCustomDumpers();
 | 
						|
 | 
						|
    // jump over well-known frames
 | 
						|
    static int stepCounter = 0;
 | 
						|
    if (qq->skipKnownFrames()) {
 | 
						|
        if (reason == "end-stepping-range" || reason == "function-finished") {
 | 
						|
            GdbMi frame = data.findChild("frame");
 | 
						|
            //qDebug() << frame.toString();
 | 
						|
            m_currentFrame = frame.findChild("addr").data() + '%' +
 | 
						|
                 frame.findChild("func").data() + '%';
 | 
						|
 | 
						|
            QString funcName = frame.findChild("func").data();
 | 
						|
            QString fileName = frame.findChild("file").data();
 | 
						|
            if (isLeavableFunction(funcName, fileName)) {
 | 
						|
                //qDebug() << "LEAVING" << funcName;
 | 
						|
                ++stepCounter;
 | 
						|
                q->stepOutExec();
 | 
						|
                //stepExec();
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            if (isSkippableFunction(funcName, fileName)) {
 | 
						|
                //qDebug() << "SKIPPING" << funcName;
 | 
						|
                ++stepCounter;
 | 
						|
                q->stepExec();
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            //if (stepCounter)
 | 
						|
            //    qDebug() << "STEPCOUNTER:" << stepCounter;
 | 
						|
            stepCounter = 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (isStoppedReason(reason) || reason.isEmpty()) {
 | 
						|
        // Need another round trip
 | 
						|
        if (reason == "breakpoint-hit") {
 | 
						|
            q->showStatusMessage(tr("Stopped at breakpoint"));
 | 
						|
            GdbMi frame = data.findChild("frame");
 | 
						|
            //qDebug() << frame.toString();
 | 
						|
            m_currentFrame = frame.findChild("addr").data() + '%' +
 | 
						|
                 frame.findChild("func").data() + '%';
 | 
						|
 | 
						|
            QApplication::alert(q->mainWindow(), 3000);
 | 
						|
            sendCommand("-file-list-exec-source-files", GdbQuerySources);
 | 
						|
            sendCommand("-break-list", BreakList);
 | 
						|
            QVariant var = QVariant::fromValue<GdbMi>(data);
 | 
						|
            sendCommand("p 0", GdbAsyncOutput2, var);  // dummy
 | 
						|
        } else {
 | 
						|
            q->showStatusMessage(tr("Stopped: \"%1\"").arg(reason));
 | 
						|
            handleAsyncOutput2(data);
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    qDebug() << "STOPPED FOR UNKNOWN REASON" << data.toString();
 | 
						|
    // Ignore it. Will be handled with full response later in the
 | 
						|
    // JumpToLine or RunToFunction handlers
 | 
						|
#if 1
 | 
						|
    // 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"}
 | 
						|
    //
 | 
						|
    // MAC yields sometimes:
 | 
						|
    // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142",
 | 
						|
    // system="0.00136",start="1218810678.805432",end="1218810678.812011"}
 | 
						|
    q->resetLocation();
 | 
						|
    qq->notifyInferiorStopped();
 | 
						|
    q->showStatusMessage(tr("Run to Function finished. Stopped."));
 | 
						|
    GdbMi frame = data.findChild("frame");
 | 
						|
    QString file = frame.findChild("fullname").data();
 | 
						|
    int line = frame.findChild("line").data().toInt();
 | 
						|
    qDebug() << "HIT: " << file << line << " IN " << frame.toString()
 | 
						|
        << " -- " << data.toString();
 | 
						|
    q->gotoLocation(file, line, true);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void GdbEngine::handleAsyncOutput2(const GdbMi &data)
 | 
						|
{
 | 
						|
    qq->notifyInferiorStopped();
 | 
						|
 | 
						|
    //
 | 
						|
    // Breakpoints
 | 
						|
    //
 | 
						|
    //qDebug() << "BREAK ASYNC: " << output.toString();
 | 
						|
    //sendListBreakpoints();
 | 
						|
    //attemptBreakpointSynchronization();
 | 
						|
    //if (m_breakListOnStopNeeded)
 | 
						|
    //    sendListBreakpoints();
 | 
						|
 | 
						|
    // something reasonably 'invariant'
 | 
						|
    // Linux:
 | 
						|
    //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
 | 
						|
    //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
 | 
						|
    //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
 | 
						|
    //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp"
 | 
						|
    //,line="261"}"
 | 
						|
    // Mac: (but only sometimes)
 | 
						|
    // "82*stopped,bkpt={number="0",type="step
 | 
						|
    // resume",disp="keep",enabled="y",addr="0x43127171",at="<Find::
 | 
						|
    // Internal::FindToolWindow::invokeFindIncremental()
 | 
						|
    // +225>",thread="1",shlib="/Users/epreuss/dev/ide/main/bin/
 | 
						|
    // workbench.app/Contents/PlugIns/Trolltech/libFind.1.0.0.dylib",
 | 
						|
    // frame="0xbfffd800",thread="1",times="1"},
 | 
						|
 | 
						|
    //
 | 
						|
    // Stack
 | 
						|
    //
 | 
						|
    qq->stackHandler()->setCurrentIndex(0);
 | 
						|
    updateLocals(); // Quick shot
 | 
						|
 | 
						|
    int currentId = data.findChild("thread-id").data().toInt();
 | 
						|
    sendSynchronizedCommand("-stack-list-frames", StackListFrames);
 | 
						|
    if (supportsThreads())
 | 
						|
        sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId);
 | 
						|
 | 
						|
    //
 | 
						|
    // Disassembler
 | 
						|
    //
 | 
						|
    // Linux:
 | 
						|
    //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
 | 
						|
    //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
 | 
						|
    //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
 | 
						|
    //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}"
 | 
						|
    // Mac: (but only sometimes)
 | 
						|
    m_address = data.findChild("frame").findChild("addr").data();
 | 
						|
    qq->reloadDisassembler();
 | 
						|
 | 
						|
    //
 | 
						|
    // Registers
 | 
						|
    //
 | 
						|
    qq->reloadRegisters();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleShowVersion(const GdbResultRecord &response)
 | 
						|
{
 | 
						|
    if (response.resultClass == GdbResultDone) {
 | 
						|
        m_gdbVersion = 100;
 | 
						|
        QString msg = response.data.findChild("consolestreamoutput").data();
 | 
						|
        QRegExp supported("GNU gdb(.*) (\\d+)\\.(\\d+)(\\.(\\d+))?");
 | 
						|
        if (supported.indexIn(msg) == -1) {
 | 
						|
            qDebug() << "UNSUPPORTED GDB VERSION " << msg;
 | 
						|
            QStringList list = msg.split("\n");
 | 
						|
            while (list.size() > 2)
 | 
						|
                list.removeLast();
 | 
						|
            msg = tr("The debugger you are using identifies itself as:")
 | 
						|
                + "<p><p>" + list.join("<br>") + "<p><p>"
 | 
						|
                + tr("This version is not officially supported by Qt Creator.\n"
 | 
						|
                     "Debugging will most likely not work well.\n"
 | 
						|
                     "Using gdb 6.7 or later is strongly recommended.");
 | 
						|
#if 0
 | 
						|
            // ugly, but 'Show again' check box...
 | 
						|
            static QErrorMessage *err = new QErrorMessage(m_mainWindow);
 | 
						|
            err->setMinimumSize(400, 300);
 | 
						|
            err->showMessage(msg);
 | 
						|
#else
 | 
						|
            //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
 | 
						|
#endif
 | 
						|
        } else {
 | 
						|
            m_gdbVersion = 10000 * supported.cap(2).toInt()
 | 
						|
                         +   100 * supported.cap(3).toInt()
 | 
						|
                         +     1 * supported.cap(5).toInt();
 | 
						|
            //qDebug() << "GDB VERSION " << m_gdbVersion;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleFileExecAndSymbols
 | 
						|
    (const GdbResultRecord &response)
 | 
						|
{
 | 
						|
    if (response.resultClass == GdbResultDone) {
 | 
						|
        //m_breakHandler->clearBreakMarkers();
 | 
						|
    } else if (response.resultClass == GdbResultError) {
 | 
						|
        QString msg = response.data.findChild("msg").data();
 | 
						|
        QMessageBox::critical(q->mainWindow(), tr("Error"),
 | 
						|
            tr("Starting executable failed:\n") + msg);
 | 
						|
        QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
 | 
						|
        interruptInferior();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleExecRun(const GdbResultRecord &response)
 | 
						|
{
 | 
						|
    if (response.resultClass == GdbResultRunning) {
 | 
						|
        qq->notifyInferiorRunning();
 | 
						|
        q->showStatusMessage(tr("Running..."));
 | 
						|
        //reloadModules();
 | 
						|
    } else if (response.resultClass == GdbResultError) {
 | 
						|
        QString msg = response.data.findChild("msg").data();
 | 
						|
        if (msg == "Cannot find bounds of current function") {
 | 
						|
            qq->notifyInferiorStopped();
 | 
						|
            //q->showStatusMessage(tr("No debug information available. "
 | 
						|
            //  "Leaving function..."));
 | 
						|
            //stepOutExec();
 | 
						|
        } else {
 | 
						|
            QMessageBox::critical(q->mainWindow(), tr("Error"),
 | 
						|
                tr("Starting executable failed:\n") + msg);
 | 
						|
            QTC_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
 | 
						|
            interruptInferior();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::queryFullName(const QString &fileName, QString *full)
 | 
						|
{
 | 
						|
    *full = fullName(fileName);
 | 
						|
}
 | 
						|
 | 
						|
QString GdbEngine::shortName(const QString &fullName)
 | 
						|
{
 | 
						|
    return m_fullToShortName.value(fullName, QString());
 | 
						|
}
 | 
						|
 | 
						|
QString GdbEngine::fullName(const QString &fileName)
 | 
						|
{
 | 
						|
    //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
 | 
						|
    if (fileName.isEmpty())
 | 
						|
        return QString();
 | 
						|
    QString full = m_shortToFullName.value(fileName, QString());
 | 
						|
    //qDebug() << "RESOLVING: " << fileName << full;
 | 
						|
    if (!full.isEmpty())
 | 
						|
        return full;
 | 
						|
    QFileInfo fi(fileName);
 | 
						|
    if (!fi.isReadable())
 | 
						|
        return QString();
 | 
						|
    full = fi.absoluteFilePath();
 | 
						|
    #ifdef Q_OS_WIN
 | 
						|
    full = QDir::cleanPath(full);
 | 
						|
    #endif
 | 
						|
    //qDebug() << "STORING: " << fileName << full;
 | 
						|
    m_shortToFullName[fileName] = full;
 | 
						|
    m_fullToShortName[full] = fileName;
 | 
						|
    return full;
 | 
						|
}
 | 
						|
 | 
						|
QString GdbEngine::fullName(const QStringList &candidates)
 | 
						|
{
 | 
						|
    QString full;
 | 
						|
    foreach (const QString &fileName, candidates) {
 | 
						|
        full = fullName(fileName);
 | 
						|
        if (!full.isEmpty())
 | 
						|
            return full;
 | 
						|
    }
 | 
						|
    foreach (const QString &fileName, candidates) {
 | 
						|
        if (!fileName.isEmpty())
 | 
						|
            return fileName;
 | 
						|
    }
 | 
						|
    return full;
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::shutdown()
 | 
						|
{
 | 
						|
    exitDebugger();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::exitDebugger()
 | 
						|
{
 | 
						|
    //qDebug() << "EXITING: " << m_gdbProc.state();
 | 
						|
    if (m_gdbProc.state() == QProcess::Starting)
 | 
						|
        m_gdbProc.waitForStarted();
 | 
						|
    if (m_gdbProc.state() == QProcess::Running) {
 | 
						|
        interruptInferior();
 | 
						|
        sendCommand("kill");
 | 
						|
        sendCommand("-gdb-exit");
 | 
						|
        // 20s can easily happen when loading webkit debug information
 | 
						|
        m_gdbProc.waitForFinished(20000);
 | 
						|
        if (m_gdbProc.state() != QProcess::Running) {
 | 
						|
            m_gdbProc.terminate();
 | 
						|
            m_gdbProc.waitForFinished(20000);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (m_gdbProc.state() != QProcess::NotRunning)
 | 
						|
        qDebug() << "PROBLEM STOPPING DEBUGGER";
 | 
						|
 | 
						|
    m_shortToFullName.clear();
 | 
						|
    m_fullToShortName.clear();
 | 
						|
    m_varToType.clear();
 | 
						|
    m_dataDumperState = DataDumperUninitialized;
 | 
						|
    m_shared = 0;
 | 
						|
    m_outputCollector.shutdown();
 | 
						|
    //q->settings()->m_debugDumpers = false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int GdbEngine::currentFrame() const
 | 
						|
{
 | 
						|
    return qq->stackHandler()->currentIndex();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool GdbEngine::startDebugger()
 | 
						|
{
 | 
						|
    QStringList gdbArgs;
 | 
						|
 | 
						|
    QFileInfo fi(q->m_executable);
 | 
						|
    QString fileName = '"' + fi.absoluteFilePath() + '"';
 | 
						|
 | 
						|
    if (m_gdbProc.state() != QProcess::NotRunning) {
 | 
						|
        qDebug() << "GDB IS ALREADY RUNNING!";
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!m_outputCollector.listen()) {
 | 
						|
        QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
 | 
						|
                              tr("Cannot set up communication with child process: %1")
 | 
						|
                              .arg(m_outputCollector.errorString()));
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName());
 | 
						|
 | 
						|
    //gdbArgs.prepend(QLatin1String("--quiet"));
 | 
						|
    gdbArgs.prepend(QLatin1String("mi"));
 | 
						|
    gdbArgs.prepend(QLatin1String("-i"));
 | 
						|
 | 
						|
    if (!q->m_workingDir.isEmpty())
 | 
						|
        m_gdbProc.setWorkingDirectory(q->m_workingDir);
 | 
						|
    if (!q->m_environment.isEmpty())
 | 
						|
        m_gdbProc.setEnvironment(q->m_environment);
 | 
						|
 | 
						|
    #if 0
 | 
						|
    qDebug() << "Command: " << q->settings()->m_gdbCmd;
 | 
						|
    qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory();
 | 
						|
    qDebug() << "ScriptFile: " << q->settings()->m_scriptFile;
 | 
						|
    qDebug() << "Environment: " << m_gdbProc.environment();
 | 
						|
    qDebug() << "Arguments: " << gdbArgs;
 | 
						|
    qDebug() << "BuildDir: " << q->m_buildDir;
 | 
						|
    qDebug() << "ExeFile: " << q->m_executable;
 | 
						|
    #endif
 | 
						|
 | 
						|
    q->showStatusMessage(tr("Starting Debugger"));
 | 
						|
    emit gdbInputAvailable(QString(), q->settings()->m_gdbCmd + ' ' + gdbArgs.join(" "));
 | 
						|
 | 
						|
    m_gdbProc.start(q->settings()->m_gdbCmd, gdbArgs);
 | 
						|
    m_gdbProc.waitForStarted();
 | 
						|
 | 
						|
    if (m_gdbProc.state() != QProcess::Running) {
 | 
						|
        QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
 | 
						|
                              tr("Cannot start debugger: %1").arg(m_gdbProc.errorString()));
 | 
						|
        m_outputCollector.shutdown();
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    q->showStatusMessage(tr("Gdb Running"));
 | 
						|
 | 
						|
    sendCommand("show version", GdbShowVersion);
 | 
						|
    if (qq->useFastStart()) {
 | 
						|
        sendCommand("set auto-solib-add off");
 | 
						|
        sendCommand("set stop-on-solib-events 1");
 | 
						|
    }
 | 
						|
    //sendCommand("-enable-timings");
 | 
						|
    //sendCommand("set stop-on-solib-events 1");
 | 
						|
    sendCommand("set print static-members off"); // Seemingly doesn't work.
 | 
						|
    //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
 | 
						|
    //sendCommand("define hook-stop\nprint 4\nend");
 | 
						|
    //sendCommand("define hookpost-stop\nprint 5\nend");
 | 
						|
    //sendCommand("define hook-call\nprint 6\nend");
 | 
						|
    //sendCommand("define hookpost-call\nprint 7\nend");
 | 
						|
    //sendCommand("set print object on"); // works with CLI, but not MI
 | 
						|
    //sendCommand("set step-mode on");  // we can't work with that yes
 | 
						|
    //sendCommand("set exec-done-display on");
 | 
						|
    //sendCommand("set print pretty on");
 | 
						|
    //sendCommand("set confirm off");
 | 
						|
    //sendCommand("set pagination off");
 | 
						|
    sendCommand("set breakpoint pending on", BreakEnablePending);
 | 
						|
    sendCommand("set print elements 10000");
 | 
						|
 | 
						|
    // one of the following is needed to prevent crashes in gdb on code like:
 | 
						|
    //  template <class T> T foo() { return T(0); }
 | 
						|
    //  int main() { return foo<int>(); }
 | 
						|
    //  (gdb) call 'int foo<int>'()
 | 
						|
    //  /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
 | 
						|
    sendCommand("set overload-resolution off");
 | 
						|
    //sendCommand("set demangle-style none");
 | 
						|
 | 
						|
    // From the docs:
 | 
						|
    //  Stop means reenter debugger if this signal happens (implies print).
 | 
						|
    //  Print means print a message if this signal happens.
 | 
						|
    //  Pass means let program see this signal;
 | 
						|
    //  otherwise program doesn't know.
 | 
						|
    //  Pass and Stop may be combined.
 | 
						|
    // We need "print" as otherwise we would get no feedback whatsoever
 | 
						|
    // Custom Dumper crashs which happen regularily for when accessing
 | 
						|
    // uninitialized variables.
 | 
						|
    sendCommand("handle SIGSEGV nopass stop print");
 | 
						|
 | 
						|
    // This is useful to kill the inferior whenever gdb dies.
 | 
						|
    //sendCommand("handle SIGTERM pass nostop print");
 | 
						|
 | 
						|
    sendCommand("set unwindonsignal on");
 | 
						|
    sendCommand("pwd", GdbQueryPwd);
 | 
						|
 | 
						|
    #ifdef Q_OS_MAC
 | 
						|
    sendCommand("-gdb-set inferior-auto-start-cfm off");
 | 
						|
    sendCommand("-gdb-set sharedLibrary load-rules "
 | 
						|
            "dyld \".*libSystem.*\" all "
 | 
						|
            "dyld \".*libauto.*\" all "
 | 
						|
            "dyld \".*AppKit.*\" all "
 | 
						|
            "dyld \".*PBGDBIntrospectionSupport.*\" all "
 | 
						|
            "dyld \".*Foundation.*\" all "
 | 
						|
            "dyld \".*CFDataFormatters.*\" all "
 | 
						|
            "dyld \".*libobjc.*\" all "
 | 
						|
            "dyld \".*CarbonDataFormatters.*\" all");
 | 
						|
    #endif
 | 
						|
 | 
						|
    QString scriptFileName = q->settings()->m_scriptFile;
 | 
						|
    if (!scriptFileName.isEmpty()) {
 | 
						|
        QFile scriptFile(scriptFileName);
 | 
						|
        if (scriptFile.open(QIODevice::ReadOnly)) {
 | 
						|
            sendCommand("source " + scriptFileName);
 | 
						|
        } else {
 | 
						|
            QMessageBox::warning(q->mainWindow(),
 | 
						|
            tr("Cannot find debugger initialization script"),
 | 
						|
            tr("The debugger settings point to a script file at '%1' "
 | 
						|
               "which is not accessible. If a script file is not needed, "
 | 
						|
               "consider clearing that entry to avoid this warning. "
 | 
						|
              ).arg(scriptFileName));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (q->startMode() == q->attachExternal) {
 | 
						|
        sendCommand("attach " + QString::number(q->m_attachedPID));
 | 
						|
    }
 | 
						|
 | 
						|
    if (q->startMode() == q->startInternal || q->startMode() == q->startExternal) {
 | 
						|
        sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols);
 | 
						|
        #ifdef Q_OS_MAC
 | 
						|
        sendCommand("sharedlibrary apply-load-rules all");
 | 
						|
        #endif
 | 
						|
        sendCommand("-file-list-exec-source-files", GdbQuerySources);
 | 
						|
        //sendCommand("-gdb-set stop-on-solib-events 1");
 | 
						|
    }
 | 
						|
 | 
						|
    sendCommand("-data-list-register-names", RegisterListNames);
 | 
						|
 | 
						|
    // set all to "pending"
 | 
						|
    if (q->startMode() == q->attachExternal)
 | 
						|
        qq->breakHandler()->removeAllBreakpoints();
 | 
						|
    else
 | 
						|
        qq->breakHandler()->setAllPending();
 | 
						|
 | 
						|
    QTimer::singleShot(0, this, SLOT(attemptBreakpointSynchronization()));
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::continueInferior()
 | 
						|
{
 | 
						|
    q->resetLocation();
 | 
						|
    setTokenBarrier();
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    emit gdbInputAvailable(QString(), QString());
 | 
						|
    sendCommand("-exec-continue", GdbExecContinue);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::runInferior()
 | 
						|
{
 | 
						|
    q->resetLocation();
 | 
						|
    // FIXME: this ignores important startup messages
 | 
						|
    setTokenBarrier();
 | 
						|
    if (!q->m_processArgs.isEmpty())
 | 
						|
        sendCommand("-exec-arguments " + q->m_processArgs.join(" "));
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    emit gdbInputAvailable(QString(), QString());
 | 
						|
    sendCommand("-exec-run", GdbExecRun);
 | 
						|
#if defined(Q_OS_WIN)
 | 
						|
    sendCommand("info proc", GdbInfoProc);
 | 
						|
#endif
 | 
						|
#if defined(Q_OS_LINUX)
 | 
						|
    sendCommand("info proc", GdbInfoProc);
 | 
						|
#endif
 | 
						|
#if defined(Q_OS_MAC)
 | 
						|
    sendCommand("info pid", GdbInfoProc, QVariant(), true);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::stepExec()
 | 
						|
{
 | 
						|
    setTokenBarrier();
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    emit gdbInputAvailable(QString(), QString());
 | 
						|
    sendCommand("-exec-step", GdbExecStep);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::stepIExec()
 | 
						|
{
 | 
						|
    setTokenBarrier();
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    sendCommand("-exec-step-instruction", GdbExecStepI);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::stepOutExec()
 | 
						|
{
 | 
						|
    setTokenBarrier();
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    sendCommand("-exec-finish", GdbExecFinish);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::nextExec()
 | 
						|
{
 | 
						|
    setTokenBarrier();
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    emit gdbInputAvailable(QString(), QString());
 | 
						|
    sendCommand("-exec-next", GdbExecNext);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::nextIExec()
 | 
						|
{
 | 
						|
    setTokenBarrier();
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    sendCommand("-exec-next-instruction", GdbExecNextI);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
 | 
						|
{
 | 
						|
    setTokenBarrier();
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber));
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::runToFunctionExec(const QString &functionName)
 | 
						|
{
 | 
						|
    setTokenBarrier();
 | 
						|
    sendCommand("-break-insert -t " + functionName);
 | 
						|
    qq->notifyInferiorRunningRequested();
 | 
						|
    sendCommand("-exec-continue", GdbExecRunToFunction);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
 | 
						|
{
 | 
						|
#if 1
 | 
						|
    // not available everywhere?
 | 
						|
    //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber));
 | 
						|
    sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber));
 | 
						|
    sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
 | 
						|
    // will produce something like
 | 
						|
    //  &"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
 | 
						|
    //  ~"Continuing at 0x4058f3."
 | 
						|
    //  ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
 | 
						|
    //  ~"242\t x *= 2;"
 | 
						|
    //  23^done"
 | 
						|
    q->gotoLocation(fileName, lineNumber, true);
 | 
						|
    //setBreakpoint();
 | 
						|
    //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
 | 
						|
#else
 | 
						|
    q->gotoLocation(fileName, lineNumber, true);
 | 
						|
    setBreakpoint(fileName, lineNumber);
 | 
						|
    sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
    \fn void GdbEngine::setTokenBarrier()
 | 
						|
    \brief Sets up internal structures to handle a new debugger turn.
 | 
						|
 | 
						|
    This method is called at the beginnign of all step/next/finish etc.
 | 
						|
    debugger functions.
 | 
						|
*/
 | 
						|
 | 
						|
void GdbEngine::setTokenBarrier()
 | 
						|
{
 | 
						|
    m_oldestAcceptableToken = currentToken();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::setDebugDumpers(bool on)
 | 
						|
{
 | 
						|
    if (on) {
 | 
						|
        qDebug() << "SWITCHING ON DUMPER DEBUGGING";
 | 
						|
        sendCommand("set unwindonsignal off");
 | 
						|
        q->breakByFunction("qDumpObjectData440");
 | 
						|
        //updateLocals();
 | 
						|
    } else {
 | 
						|
        qDebug() << "SWITCHING OFF DUMPER DEBUGGING";
 | 
						|
        sendCommand("set unwindonsignal on");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Breakpoint specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
 | 
						|
{
 | 
						|
    if (!bkpt.isValid())
 | 
						|
        return;
 | 
						|
    if (!data)
 | 
						|
        return;
 | 
						|
    data->pending = false;
 | 
						|
    data->bpMultiple = false;
 | 
						|
    data->bpCondition.clear();
 | 
						|
    QStringList files;
 | 
						|
    foreach (const GdbMi &child, bkpt.children()) {
 | 
						|
        if (child.hasName("number")) {
 | 
						|
            data->bpNumber = child.data();
 | 
						|
        } else if (child.hasName("func")) {
 | 
						|
            data->bpFuncName = child.data();
 | 
						|
        } else if (child.hasName("addr")) {
 | 
						|
            // <MULTIPLE> happens in constructors. In this case there are
 | 
						|
            // _two_ fields named "addr" in the response. On Linux that is...
 | 
						|
            if (child.data() == "<MULTIPLE>")
 | 
						|
                data->bpMultiple = true;
 | 
						|
            else
 | 
						|
                data->bpAddress = child.data();
 | 
						|
        } else if (child.hasName("file")) {
 | 
						|
            files.append(child.data());
 | 
						|
        } else if (child.hasName("fullname")) {
 | 
						|
            QString fullName = child.data();
 | 
						|
            #ifdef Q_OS_WIN
 | 
						|
            fullName = QDir::cleanPath(fullName);
 | 
						|
            #endif
 | 
						|
            files.prepend(fullName);
 | 
						|
        } else if (child.hasName("line")) {
 | 
						|
            data->bpLineNumber = child.data();
 | 
						|
            if (child.data().toInt())
 | 
						|
                data->markerLineNumber = child.data().toInt();
 | 
						|
        } else if (child.hasName("cond")) {
 | 
						|
            data->bpCondition = child.data();
 | 
						|
            // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
 | 
						|
            if (data->bpCondition != data->condition && data->conditionsMatch())
 | 
						|
                data->condition = data->bpCondition;
 | 
						|
        }
 | 
						|
        else if (child.hasName("pending")) {
 | 
						|
            data->pending = true;
 | 
						|
            int pos = child.data().lastIndexOf(':');
 | 
						|
            if (pos > 0) {
 | 
						|
                data->bpLineNumber = child.data().mid(pos + 1);
 | 
						|
                data->markerLineNumber = child.data().mid(pos + 1).toInt();
 | 
						|
                files.prepend(child.data().left(pos));
 | 
						|
            } else {
 | 
						|
                files.prepend(child.data());
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // This field is not present.  Contents needs to be parsed from
 | 
						|
    // the plain "ignore" response.
 | 
						|
    //else if (child.hasName("ignore"))
 | 
						|
    //    data->bpIgnoreCount = child.data();
 | 
						|
 | 
						|
    QString name = fullName(files);
 | 
						|
    if (data->bpFileName.isEmpty())
 | 
						|
        data->bpFileName = name;
 | 
						|
    if (data->markerFileName.isEmpty())
 | 
						|
        data->markerFileName = name;
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::sendInsertBreakpoint(int index)
 | 
						|
{
 | 
						|
    const BreakpointData *data = qq->breakHandler()->at(index);
 | 
						|
    QString where;
 | 
						|
    if (data->funcName.isEmpty()) {
 | 
						|
        where = data->fileName;
 | 
						|
#ifdef Q_OS_MAC
 | 
						|
        // full names do not work on Mac/MI
 | 
						|
        QFileInfo fi(data->fileName);
 | 
						|
        where = fi.fileName();
 | 
						|
        //where = fi.absoluteFilePath();
 | 
						|
#endif
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
        // full names do not work on Mac/MI
 | 
						|
        QFileInfo fi(data->fileName);
 | 
						|
        where = fi.fileName();
 | 
						|
    //where = m_manager->shortName(data->fileName);
 | 
						|
        //if (where.isEmpty())
 | 
						|
        //    where = data->fileName;
 | 
						|
#endif
 | 
						|
        // we need something like   "\"file name.cpp\":100"  to
 | 
						|
        // survive the gdb command line parser with file names intact
 | 
						|
        where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\"";
 | 
						|
    } else {
 | 
						|
        where = data->funcName;
 | 
						|
    }
 | 
						|
 | 
						|
    // set up fallback in case of pending breakpoints which aren't handled
 | 
						|
    // by the MI interface
 | 
						|
#ifdef Q_OS_LINUX
 | 
						|
    QString cmd = "-break-insert ";
 | 
						|
    //if (!data->condition.isEmpty())
 | 
						|
    //    cmd += "-c " + data->condition + " ";
 | 
						|
    cmd += where;
 | 
						|
#endif
 | 
						|
#ifdef Q_OS_MAC
 | 
						|
    QString cmd = "-break-insert -l -1 ";
 | 
						|
    //if (!data->condition.isEmpty())
 | 
						|
    //    cmd += "-c " + data->condition + " ";
 | 
						|
    cmd += where;
 | 
						|
#endif
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
    QString cmd = "-break-insert ";
 | 
						|
    //if (!data->condition.isEmpty())
 | 
						|
    //    cmd += "-c " + data->condition + " ";
 | 
						|
    cmd += where;
 | 
						|
#endif
 | 
						|
    sendCommand(cmd, BreakInsert, index, true);
 | 
						|
    //processQueueAndContinue();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleBreakList(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
 | 
						|
    // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
 | 
						|
    // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
 | 
						|
    //  addr="0x000000000040109e",func="main",file="app.cpp",
 | 
						|
    //  fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
 | 
						|
    //  line="11",times="1"},
 | 
						|
    //  bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
 | 
						|
    //  addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
 | 
						|
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        GdbMi table = record.data.findChild("BreakpointTable");
 | 
						|
        if (table.isValid())
 | 
						|
            handleBreakList(table);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleBreakList(const GdbMi &table)
 | 
						|
{
 | 
						|
    //qDebug() << "GdbEngine::handleOutput: table: "
 | 
						|
    //  << table.toString();
 | 
						|
    GdbMi body = table.findChild("body");
 | 
						|
    //qDebug() << "GdbEngine::handleOutput: body: "
 | 
						|
    //  << body.toString();
 | 
						|
    QList<GdbMi> bkpts;
 | 
						|
    if (body.isValid()) {
 | 
						|
        // Non-Mac
 | 
						|
        bkpts = body.children();
 | 
						|
    } else {
 | 
						|
        // Mac
 | 
						|
        bkpts = table.children();
 | 
						|
        // remove the 'hdr' and artificial items
 | 
						|
        //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS";
 | 
						|
        for (int i = bkpts.size(); --i >= 0; ) {
 | 
						|
            int num = bkpts.at(i).findChild("number").data().toInt();
 | 
						|
            if (num <= 0) {
 | 
						|
                //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
 | 
						|
                bkpts.removeAt(i);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
 | 
						|
    }
 | 
						|
 | 
						|
    BreakHandler *handler = qq->breakHandler();
 | 
						|
    for (int index = 0; index != bkpts.size(); ++index) {
 | 
						|
        BreakpointData temp(handler);
 | 
						|
        breakpointDataFromOutput(&temp, bkpts.at(index));
 | 
						|
        int found = handler->findBreakpoint(temp);
 | 
						|
        if (found != -1)
 | 
						|
            breakpointDataFromOutput(handler->at(found), bkpts.at(index));
 | 
						|
        //else
 | 
						|
            //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
 | 
						|
    }
 | 
						|
 | 
						|
    attemptBreakpointSynchronization();
 | 
						|
    handler->updateMarkers();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index)
 | 
						|
{
 | 
						|
    // gdb 6.8:
 | 
						|
    // ignore 2 0:
 | 
						|
    // ~"Will stop next time breakpoint 2 is reached.\n"
 | 
						|
    // 28^done
 | 
						|
    // ignore 2 12:
 | 
						|
    // &"ignore 2 12\n"
 | 
						|
    // ~"Will ignore next 12 crossings of breakpoint 2.\n"
 | 
						|
    // 29^done
 | 
						|
    //
 | 
						|
    // gdb 6.3 does not produce any console output
 | 
						|
    BreakHandler *handler = qq->breakHandler();
 | 
						|
    if (record.resultClass == GdbResultDone && index < handler->size()) {
 | 
						|
        QString msg = record.data.findChild("consolestreamoutput").data();
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        //if (msg.contains("Will stop next time breakpoint")) {
 | 
						|
        //    data->bpIgnoreCount = "0";
 | 
						|
        //} else if (msg.contains("Will ignore next")) {
 | 
						|
        //    data->bpIgnoreCount = data->ignoreCount;
 | 
						|
        //}
 | 
						|
        // FIXME: this assumes it is doing the right thing...
 | 
						|
        data->bpIgnoreCount = data->ignoreCount;
 | 
						|
        attemptBreakpointSynchronization();
 | 
						|
        handler->updateMarkers();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index)
 | 
						|
{
 | 
						|
    BreakHandler *handler = qq->breakHandler();
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        // we just assume it was successful. otherwise we had to parse
 | 
						|
        // the output stream data
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition;
 | 
						|
        data->bpCondition = data->condition;
 | 
						|
        attemptBreakpointSynchronization();
 | 
						|
        handler->updateMarkers();
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        QString msg = record.data.findChild("msg").data();
 | 
						|
        // happens on Mac
 | 
						|
        if (1 || msg.startsWith("Error parsing breakpoint condition. "
 | 
						|
                " Will try again when we hit the breakpoint.")) {
 | 
						|
            BreakpointData *data = handler->at(index);
 | 
						|
            //qDebug() << "ERROR BREAK CONDITION " << index << data->condition;
 | 
						|
            data->bpCondition = data->condition;
 | 
						|
            attemptBreakpointSynchronization();
 | 
						|
            handler->updateMarkers();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index)
 | 
						|
{
 | 
						|
    BreakHandler *handler = qq->breakHandler();
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        //qDebug() << "HANDLE BREAK INSERT " << index;
 | 
						|
//#ifdef Q_OS_MAC
 | 
						|
        // interesting only on Mac?
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        GdbMi bkpt = record.data.findChild("bkpt");
 | 
						|
        //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip();
 | 
						|
        breakpointDataFromOutput(data, bkpt);
 | 
						|
//#endif
 | 
						|
        attemptBreakpointSynchronization();
 | 
						|
        handler->updateMarkers();
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        const BreakpointData *data = handler->at(index);
 | 
						|
#ifdef Q_OS_LINUX
 | 
						|
        //QString where = "\"\\\"" + data->fileName + "\\\":"
 | 
						|
        //    + data->lineNumber + "\"";
 | 
						|
        QString where = "\"" + data->fileName + "\":"
 | 
						|
            + data->lineNumber;
 | 
						|
        sendCommand("break " + where, BreakInsert1, index);
 | 
						|
#endif
 | 
						|
#ifdef Q_OS_MAC
 | 
						|
        QFileInfo fi(data->fileName);
 | 
						|
        QString where = "\"" + fi.fileName() + "\":"
 | 
						|
            + data->lineNumber;
 | 
						|
        sendCommand("break " + where, BreakInsert1, index);
 | 
						|
#endif
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
        QFileInfo fi(data->fileName);
 | 
						|
        QString where = "\"" + fi.fileName() + "\":"
 | 
						|
            + data->lineNumber;
 | 
						|
        //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
 | 
						|
        sendCommand("break " + where, BreakInsert1, index);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
 | 
						|
{
 | 
						|
    data->bpFileName = "<MULTIPLE>";
 | 
						|
 | 
						|
    //qDebug() << output;
 | 
						|
    if (output.isEmpty())
 | 
						|
        return;
 | 
						|
    // "Num     Type           Disp Enb Address            What
 | 
						|
    // 4       breakpoint     keep y   <MULTIPLE>         0x00000000004066ad
 | 
						|
    // 4.1                         y     0x00000000004066ad in CTorTester
 | 
						|
    //  at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
 | 
						|
    // - or - 
 | 
						|
    // everything on a single line on Windows for constructors of classes
 | 
						|
    // within namespaces.
 | 
						|
    // Sometimes the path is relative too.
 | 
						|
  
 | 
						|
    QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)");
 | 
						|
    re.setMinimal(true);
 | 
						|
 | 
						|
    if (re.indexIn(output) != -1) {
 | 
						|
        data->bpAddress = re.cap(1);
 | 
						|
        data->bpFuncName = re.cap(2).trimmed();
 | 
						|
        data->bpLineNumber = re.cap(4);
 | 
						|
        QString full = fullName(re.cap(3));
 | 
						|
        data->markerLineNumber = data->bpLineNumber.toInt();
 | 
						|
        data->markerFileName = full;
 | 
						|
        data->bpFileName = full;
 | 
						|
        //qDebug() << "FOUND BREAKPOINT\n" << output
 | 
						|
        //    << re.cap(1) << "\n" << re.cap(2) << "\n"
 | 
						|
        //    << re.cap(3) << "\n" << re.cap(4) << "\n";
 | 
						|
    } else {
 | 
						|
        qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
 | 
						|
        data->bpNumber = "<unavailable>";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber)
 | 
						|
{
 | 
						|
    BreakHandler *handler = qq->breakHandler();
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        // Old-style output for multiple breakpoints, presumably in a
 | 
						|
        // constructor
 | 
						|
        int found = handler->findBreakpoint(bpNumber);
 | 
						|
        if (found != -1) {
 | 
						|
            QString str = record.data.findChild("consolestreamoutput").data();
 | 
						|
            extractDataFromInfoBreak(str, handler->at(found));
 | 
						|
            handler->updateMarkers();
 | 
						|
            attemptBreakpointSynchronization(); // trigger "ready"
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index)
 | 
						|
{
 | 
						|
    BreakHandler *handler = qq->breakHandler();
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        // Pending breakpoints in dylibs on Mac only?
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        GdbMi bkpt = record.data.findChild("bkpt");
 | 
						|
        breakpointDataFromOutput(data, bkpt);
 | 
						|
        attemptBreakpointSynchronization(); // trigger "ready"
 | 
						|
        handler->updateMarkers();
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP";
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        data->bpNumber = "<unavailable>";
 | 
						|
        attemptBreakpointSynchronization(); // trigger "ready"
 | 
						|
        handler->updateMarkers();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::attemptBreakpointSynchronization()
 | 
						|
{
 | 
						|
    BreakHandler *handler = qq->breakHandler();
 | 
						|
    //qDebug() << "BREAKPOINT SYNCHRONIZATION ";
 | 
						|
 | 
						|
    foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
 | 
						|
        //qDebug() << " SYNCHRONIZATION REMOVING" << data;
 | 
						|
        QString bpNumber = data->bpNumber;
 | 
						|
        if (!bpNumber.trimmed().isEmpty())
 | 
						|
            sendCommand("-break-delete " + bpNumber, BreakDelete, 0, true);
 | 
						|
        //else
 | 
						|
        //    qDebug() << "BP HAS NO NUMBER: " << data->markerFileName;
 | 
						|
        delete data;
 | 
						|
    }
 | 
						|
 | 
						|
    bool updateNeeded = false;
 | 
						|
 | 
						|
    for (int index = 0; index != handler->size(); ++index) {
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        // multiple breakpoints?
 | 
						|
        if (data->bpMultiple && data->bpFileName.isEmpty()) {
 | 
						|
            sendCommand(QString("info break %1").arg(data->bpNumber),
 | 
						|
                BreakInfo, data->bpNumber.toInt());
 | 
						|
            updateNeeded = true;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for (int index = 0; index != handler->size(); ++index) {
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        // unset breakpoints?
 | 
						|
        if (data->bpNumber.isEmpty()) {
 | 
						|
            data->bpNumber = " ";
 | 
						|
            sendInsertBreakpoint(index);
 | 
						|
            //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT";
 | 
						|
            updateNeeded = true;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!updateNeeded) {
 | 
						|
        for (int index = 0; index != handler->size(); ++index) {
 | 
						|
            BreakpointData *data = handler->at(index);
 | 
						|
            // update conditions if needed
 | 
						|
            if (data->bpNumber.toInt() && data->condition != data->bpCondition
 | 
						|
                   && !data->conditionsMatch()) {
 | 
						|
                sendCommand(QString("condition %1 %2").arg(data->bpNumber)
 | 
						|
                    .arg(data->condition), BreakCondition, index);
 | 
						|
                //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION"
 | 
						|
                //    << data->condition << data->bpCondition;
 | 
						|
                updateNeeded = true;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            // update ignorecount if needed
 | 
						|
            if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) {
 | 
						|
                sendCommand(QString("ignore %1 %2").arg(data->bpNumber)
 | 
						|
                    .arg(data->ignoreCount), BreakIgnore, index);
 | 
						|
                updateNeeded = true;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for (int index = 0; index != handler->size(); ++index) {
 | 
						|
        // happens sometimes on Mac. Brush over symptoms
 | 
						|
        BreakpointData *data = handler->at(index);
 | 
						|
        if (data->markerFileName.startsWith("../")) {
 | 
						|
            data->markerFileName = fullName(data->markerFileName);
 | 
						|
            handler->updateMarkers();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (updateNeeded) {
 | 
						|
        //interruptAndContinue();
 | 
						|
        //sendListBreakpoints();
 | 
						|
    }
 | 
						|
 | 
						|
    if (!updateNeeded && q->status() == DebuggerProcessStartingUp)
 | 
						|
        qq->notifyStartupFinished();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Disassembler specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void GdbEngine::reloadDisassembler()
 | 
						|
{
 | 
						|
    emit sendCommand("disassemble", DisassemblerList, m_address);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleDisassemblerList(const GdbResultRecord &record,
 | 
						|
    const QString &cookie)
 | 
						|
{
 | 
						|
    QList<DisassemblerLine> lines;
 | 
						|
    static const QString pad = QLatin1String("    ");
 | 
						|
    int currentLine = -1;
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        QString res = record.data.findChild("consolestreamoutput").data();
 | 
						|
        QTextStream ts(&res, QIODevice::ReadOnly);
 | 
						|
        while (!ts.atEnd()) {
 | 
						|
            //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>:
 | 
						|
            //    jmpq   *2151890(%rip)    # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640>
 | 
						|
            //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>:
 | 
						|
            //    pushq  $0x4d
 | 
						|
            //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>:
 | 
						|
            //    jmpq   0x405af8 <_init+24>
 | 
						|
            //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>:
 | 
						|
            //    jmpq   *2151882(%rip)    # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648>
 | 
						|
            QString str = ts.readLine();
 | 
						|
            if (!str.startsWith(QLatin1String("0x"))) {
 | 
						|
                //qDebug() << "IGNORING DISASSEMBLER" << str;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            DisassemblerLine line;
 | 
						|
            QTextStream ts(&str, QIODevice::ReadOnly);
 | 
						|
            ts >> line.address >> line.symbol;
 | 
						|
            line.mnemonic = ts.readLine().trimmed();
 | 
						|
            if (line.symbol.endsWith(QLatin1Char(':')))
 | 
						|
                line.symbol.chop(1);
 | 
						|
            line.addressDisplay = line.address + pad;
 | 
						|
            if (line.addressDisplay.startsWith(QLatin1String("0x00000000")))
 | 
						|
                line.addressDisplay.replace(2, 8, QString());
 | 
						|
            line.symbolDisplay = line.symbol + pad;
 | 
						|
 | 
						|
            if (line.address == cookie)
 | 
						|
                currentLine = lines.size();
 | 
						|
 | 
						|
            lines.append(line);
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        DisassemblerLine line;
 | 
						|
        line.addressDisplay = tr("<could not retreive module information>");
 | 
						|
        lines.append(line);
 | 
						|
    }
 | 
						|
 | 
						|
    qq->disassemblerHandler()->setLines(lines);
 | 
						|
    if (currentLine != -1)
 | 
						|
        qq->disassemblerHandler()->setCurrentLine(currentLine);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Modules specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void GdbEngine::loadSymbols(const QString &moduleName)
 | 
						|
{
 | 
						|
    // FIXME: gdb does not understand quoted names here (tested with 6.8)
 | 
						|
    sendCommand("sharedlibrary " + dotEscape(moduleName));
 | 
						|
    reloadModules();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::loadAllSymbols()
 | 
						|
{
 | 
						|
    sendCommand("sharedlibrary .*");
 | 
						|
    reloadModules();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::reloadModules()
 | 
						|
{
 | 
						|
    sendCommand("info shared", ModulesList, QVariant());
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleModulesList(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    QList<Module> modules;
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        QString data = record.data.findChild("consolestreamoutput").data();
 | 
						|
        QTextStream ts(&data, QIODevice::ReadOnly);
 | 
						|
        while (!ts.atEnd()) {
 | 
						|
            QString line = ts.readLine();
 | 
						|
            if (!line.startsWith("0x"))
 | 
						|
                continue;
 | 
						|
            Module module;
 | 
						|
            QString symbolsRead;
 | 
						|
            QTextStream ts(&line, QIODevice::ReadOnly);
 | 
						|
            ts >> module.startAddress >> module.endAddress >> symbolsRead;
 | 
						|
            module.moduleName = ts.readLine().trimmed();
 | 
						|
            module.symbolsRead = (symbolsRead == "Yes");
 | 
						|
            modules.append(module);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    qq->modulesHandler()->setModules(modules);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Stack specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int)
 | 
						|
{
 | 
						|
    Q_UNUSED(record);
 | 
						|
    //qDebug("FIXME: StackHandler::handleOutput: SelectThread");
 | 
						|
    q->showStatusMessage(tr("Retrieving data for stack view..."), 3000);
 | 
						|
    sendCommand("-stack-list-frames", StackListFrames);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    QList<StackFrame> stackFrames;
 | 
						|
 | 
						|
    const GdbMi stack = record.data.findChild("stack");
 | 
						|
    QString dummy = stack.toString();
 | 
						|
    if (!stack.isValid()) {
 | 
						|
        qDebug() << "FIXME: stack: " << stack.toString();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    int topFrame = -1;
 | 
						|
 | 
						|
    for (int i = 0; i != stack.childCount(); ++i) {
 | 
						|
        //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
 | 
						|
        const GdbMi frameMi = stack.childAt(i);
 | 
						|
        StackFrame frame;
 | 
						|
        frame.level = i;
 | 
						|
        QStringList files;
 | 
						|
        files.append(frameMi.findChild("fullname").data());
 | 
						|
        files.append(frameMi.findChild("file").data());
 | 
						|
        frame.file = fullName(files);
 | 
						|
        frame.function = frameMi.findChild("func").data();
 | 
						|
        frame.from = frameMi.findChild("from").data();
 | 
						|
        frame.line = frameMi.findChild("line").data().toInt();
 | 
						|
        frame.address = frameMi.findChild("addr").data();
 | 
						|
 | 
						|
        stackFrames.append(frame);
 | 
						|
 | 
						|
#ifdef Q_OS_WIN
 | 
						|
        const bool isBogus =
 | 
						|
            // Assume this is wrong and points to some strange stl_algobase
 | 
						|
            // implementation. Happens on Karsten's XP system with Gdb 5.50
 | 
						|
            (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150)
 | 
						|
            // Also wrong. Happens on Vista with Gdb 5.50
 | 
						|
               || (frame.function == "operator new" && frame.line == 151);
 | 
						|
 | 
						|
        // immediately leave bogus frames
 | 
						|
        if (topFrame == -1 && isBogus) {
 | 
						|
            sendCommand("-exec-finish");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
        // Initialize top frame to the first valid frame
 | 
						|
        const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
 | 
						|
        if (isValid && topFrame == -1)
 | 
						|
            topFrame = i;
 | 
						|
    }
 | 
						|
 | 
						|
    qq->stackHandler()->setFrames(stackFrames);
 | 
						|
 | 
						|
#if 0
 | 
						|
    if (0 && topFrame != -1) {
 | 
						|
        // updates of locals already triggered early
 | 
						|
        const StackFrame &frame = qq->stackHandler()->currentFrame();
 | 
						|
        bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
 | 
						|
        if (usable)
 | 
						|
            q->gotoLocation(frame.file, frame.line, true);
 | 
						|
        else
 | 
						|
            qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
 | 
						|
    } else {
 | 
						|
        activateFrame(topFrame);
 | 
						|
    }
 | 
						|
#else
 | 
						|
    if (topFrame != -1) {
 | 
						|
        // updates of locals already triggered early
 | 
						|
        const StackFrame &frame = qq->stackHandler()->currentFrame();
 | 
						|
        bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
 | 
						|
        if (usable)
 | 
						|
            q->gotoLocation(frame.file, frame.line, true);
 | 
						|
        else
 | 
						|
            qDebug() << "FULL NAME NOT USABLE 0: " << frame.file << topFrame;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::selectThread(int index)
 | 
						|
{
 | 
						|
    //reset location arrow
 | 
						|
    q->resetLocation();
 | 
						|
 | 
						|
    ThreadsHandler *threadsHandler = qq->threadsHandler();
 | 
						|
    threadsHandler->setCurrentThread(index);
 | 
						|
 | 
						|
    QList<ThreadData> threads = threadsHandler->threads();
 | 
						|
    QTC_ASSERT(index < threads.size(), return);
 | 
						|
    int id = threads.at(index).id;
 | 
						|
    q->showStatusMessage(tr("Retrieving data for stack view..."), 10000);
 | 
						|
    sendCommand(QLatin1String("-thread-select ") + QString::number(id),
 | 
						|
        StackSelectThread);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::activateFrame(int frameIndex)
 | 
						|
{
 | 
						|
    if (q->status() != DebuggerInferiorStopped)
 | 
						|
        return;
 | 
						|
 | 
						|
    StackHandler *stackHandler = qq->stackHandler();
 | 
						|
    int oldIndex = stackHandler->currentIndex();
 | 
						|
    //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
 | 
						|
    //    << stackHandler->currentIndex();
 | 
						|
 | 
						|
    QTC_ASSERT(frameIndex < stackHandler->stackSize(), return);
 | 
						|
 | 
						|
    if (oldIndex != frameIndex) {
 | 
						|
        // Assuming this always succeeds saves a roundtrip.
 | 
						|
        // Otherwise the lines below would need to get triggered
 | 
						|
        // after a response to this -stack-select-frame here.
 | 
						|
        sendCommand("-stack-select-frame " + QString::number(frameIndex));
 | 
						|
 | 
						|
        stackHandler->setCurrentIndex(frameIndex);
 | 
						|
        updateLocals();
 | 
						|
    }
 | 
						|
 | 
						|
    const StackFrame &frame = stackHandler->currentFrame();
 | 
						|
 | 
						|
    bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
 | 
						|
    if (usable)
 | 
						|
        q->gotoLocation(frame.file, frame.line, true);
 | 
						|
    else
 | 
						|
        qDebug() << "FULL NAME NOT USABLE: " << frame.file;
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id)
 | 
						|
{
 | 
						|
    // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
 | 
						|
    const QList<GdbMi> items = record.data.findChild("thread-ids").children();
 | 
						|
    QList<ThreadData> threads;
 | 
						|
    int currentIndex = -1;
 | 
						|
    for (int index = 0, n = items.size(); index != n; ++index) {
 | 
						|
        ThreadData thread;
 | 
						|
        thread.id = items.at(index).data().toInt();
 | 
						|
        threads.append(thread);
 | 
						|
        if (thread.id == id) {
 | 
						|
            //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString();
 | 
						|
            currentIndex = index;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    ThreadsHandler *threadsHandler = qq->threadsHandler();
 | 
						|
    threadsHandler->setThreads(threads);
 | 
						|
    threadsHandler->setCurrentThread(currentIndex);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Register specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void GdbEngine::reloadRegisters()
 | 
						|
{
 | 
						|
    QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString();
 | 
						|
    sendCommand("-data-list-register-values " + format, RegisterListValues);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleRegisterListNames(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    if (record.resultClass != GdbResultDone)
 | 
						|
        return;
 | 
						|
 | 
						|
    QList<Register> registers;
 | 
						|
    foreach (const GdbMi &item, record.data.findChild("register-names").children())
 | 
						|
        registers.append(Register(item.data()));
 | 
						|
 | 
						|
    qq->registerHandler()->setRegisters(registers);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleRegisterListValues(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    if (record.resultClass != GdbResultDone)
 | 
						|
        return;
 | 
						|
 | 
						|
    QList<Register> registers = qq->registerHandler()->registers();
 | 
						|
 | 
						|
    // 24^done,register-values=[{number="0",value="0xf423f"},...]
 | 
						|
    foreach (const GdbMi &item, record.data.findChild("register-values").children()) {
 | 
						|
        int index = item.findChild("number").data().toInt();
 | 
						|
        if (index < registers.size()) {
 | 
						|
            Register ® = registers[index];
 | 
						|
            QString value = item.findChild("value").data();
 | 
						|
            reg.changed = (value != reg.value);
 | 
						|
            if (reg.changed)
 | 
						|
                reg.value = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    qq->registerHandler()->setRegisters(registers);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Thread specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
bool GdbEngine::supportsThreads() const
 | 
						|
{
 | 
						|
    // 6.3 crashes happily on -thread-list-ids. So don't use it.
 | 
						|
    // The test below is a semi-random pick, 6.8 works fine
 | 
						|
    return m_gdbVersion > 60500;
 | 
						|
}
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Tooltip specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static WatchData m_toolTip;
 | 
						|
static QString m_toolTipExpression;
 | 
						|
static QPoint m_toolTipPos;
 | 
						|
static QHash<QString, WatchData> m_toolTipCache;
 | 
						|
 | 
						|
static bool hasLetterOrNumber(const QString &exp)
 | 
						|
{
 | 
						|
    for (int i = exp.size(); --i >= 0; )
 | 
						|
        if (exp[i].isLetterOrNumber() || exp[i] == '_')
 | 
						|
            return true;
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool hasSideEffects(const QString &exp)
 | 
						|
{
 | 
						|
    // FIXME: complete?
 | 
						|
    return exp.contains("-=")
 | 
						|
        || exp.contains("+=")
 | 
						|
        || exp.contains("/=")
 | 
						|
        || exp.contains("*=")
 | 
						|
        || exp.contains("&=")
 | 
						|
        || exp.contains("|=")
 | 
						|
        || exp.contains("^=")
 | 
						|
        || exp.contains("--")
 | 
						|
        || exp.contains("++");
 | 
						|
}
 | 
						|
 | 
						|
static bool isKeyWord(const QString &exp)
 | 
						|
{
 | 
						|
    // FIXME: incomplete
 | 
						|
    return exp == QLatin1String("class")
 | 
						|
        || exp == QLatin1String("const")
 | 
						|
        || exp == QLatin1String("do")
 | 
						|
        || exp == QLatin1String("if")
 | 
						|
        || exp == QLatin1String("return")
 | 
						|
        || exp == QLatin1String("struct")
 | 
						|
        || exp == QLatin1String("template")
 | 
						|
        || exp == QLatin1String("void")
 | 
						|
        || exp == QLatin1String("volatile")
 | 
						|
        || exp == QLatin1String("while");
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
 | 
						|
{
 | 
						|
    //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
 | 
						|
    if (q->status() != DebuggerInferiorStopped) {
 | 
						|
        //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (q->settings()->m_debugDumpers) {
 | 
						|
        // minimize interference
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    m_toolTipPos = pos;
 | 
						|
    m_toolTipExpression = exp0;
 | 
						|
    QString exp = exp0;
 | 
						|
/*
 | 
						|
    if (m_toolTip.isTypePending()) {
 | 
						|
        qDebug() << "suppressing duplicated tooltip creation";
 | 
						|
        return;
 | 
						|
    }
 | 
						|
*/
 | 
						|
    if (m_toolTipCache.contains(exp)) {
 | 
						|
        const WatchData & data = m_toolTipCache[exp];
 | 
						|
        // FIXME: qq->watchHandler()->collapseChildren(data.iname);
 | 
						|
        insertData(data);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    QToolTip::hideText();
 | 
						|
    if (exp.isEmpty() || exp.startsWith("#"))  {
 | 
						|
        QToolTip::hideText();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!hasLetterOrNumber(exp)) {
 | 
						|
        QToolTip::showText(m_toolTipPos,
 | 
						|
            "'" + exp + "' contains no identifier");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (isKeyWord(exp))
 | 
						|
        return;
 | 
						|
 | 
						|
    if (exp.startsWith('"') && exp.endsWith('"'))  {
 | 
						|
        QToolTip::showText(m_toolTipPos, "String literal " + exp);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (exp.startsWith("++") || exp.startsWith("--"))
 | 
						|
        exp = exp.mid(2);
 | 
						|
 | 
						|
    if (exp.endsWith("++") || exp.endsWith("--"))
 | 
						|
        exp = exp.mid(2);
 | 
						|
 | 
						|
    if (exp.startsWith("<") || exp.startsWith("["))
 | 
						|
        return;
 | 
						|
 | 
						|
    if (hasSideEffects(exp)) {
 | 
						|
        QToolTip::showText(m_toolTipPos,
 | 
						|
            "Cowardly refusing to evaluate expression '" + exp
 | 
						|
                + "' with potential side effects");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Gdb crashes when creating a variable object with the name
 | 
						|
    // of the type of 'this'
 | 
						|
/*
 | 
						|
    for (int i = 0; i != m_currentLocals.childCount(); ++i) {
 | 
						|
        if (m_currentLocals.childAt(i).exp == "this") {
 | 
						|
            qDebug() << "THIS IN ROW " << i;
 | 
						|
            if (m_currentLocals.childAt(i).type.startsWith(exp)) {
 | 
						|
                QToolTip::showText(m_toolTipPos,
 | 
						|
                    exp + ": type of current 'this'");
 | 
						|
                qDebug() << " TOOLTIP CRASH SUPPRESSED";
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
*/
 | 
						|
 | 
						|
    //if (m_manager->status() != DebuggerInferiorStopped)
 | 
						|
    //    return;
 | 
						|
 | 
						|
    // FIXME: 'exp' can contain illegal characters
 | 
						|
    m_toolTip = WatchData();
 | 
						|
    //m_toolTip.level = 0;
 | 
						|
   // m_toolTip.row = 0;
 | 
						|
   // m_toolTip.parentIndex = 2;
 | 
						|
    m_toolTip.exp = exp;
 | 
						|
    m_toolTip.name = exp;
 | 
						|
    m_toolTip.iname = tooltipIName;
 | 
						|
    insertData(m_toolTip);
 | 
						|
    updateWatchModel2();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Watch specific stuff
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static const QString strNotInScope = QLatin1String("<not in scope>");
 | 
						|
 | 
						|
static bool isPointerType(const QString &type)
 | 
						|
{
 | 
						|
    return type.endsWith("*") || type.endsWith("* const");
 | 
						|
}
 | 
						|
 | 
						|
static bool isAccessSpecifier(const QString &str)
 | 
						|
{
 | 
						|
    static const QStringList items =
 | 
						|
        QStringList() << "private" << "protected" << "public";
 | 
						|
    return items.contains(str);
 | 
						|
}
 | 
						|
 | 
						|
static bool startsWithDigit(const QString &str)
 | 
						|
{
 | 
						|
    return !str.isEmpty() && str[0] >= '0' && str[0] <= '9';
 | 
						|
}
 | 
						|
 | 
						|
QString stripPointerType(QString type)
 | 
						|
{
 | 
						|
    if (type.endsWith("*"))
 | 
						|
        type.chop(1);
 | 
						|
    if (type.endsWith("* const"))
 | 
						|
        type.chop(7);
 | 
						|
    if (type.endsWith(' '))
 | 
						|
        type.chop(1);
 | 
						|
    return type;
 | 
						|
}
 | 
						|
 | 
						|
static QString gdbQuoteTypes(const QString &type)
 | 
						|
{
 | 
						|
    // gdb does not understand sizeof(Core::IFile*).
 | 
						|
    // "sizeof('Core::IFile*')" is also not acceptable,
 | 
						|
    // it needs to be "sizeof('Core::IFile'*)"
 | 
						|
    //
 | 
						|
    // We never will have a perfect solution here (even if we had a full blown
 | 
						|
    // C++ parser as we do not have information on what is a type and what is
 | 
						|
    // a vriable name. So "a<b>::c" could either be two comparisons of values
 | 
						|
    // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
 | 
						|
    // assume here it is the latter.
 | 
						|
    //return type;
 | 
						|
 | 
						|
    // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
 | 
						|
    // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
 | 
						|
    if (isPointerType(type))
 | 
						|
        return gdbQuoteTypes(stripPointerType(type)) + "*";
 | 
						|
 | 
						|
    QString accu;
 | 
						|
    QString result;
 | 
						|
    int templateLevel = 0;
 | 
						|
    for (int i = 0; i != type.size(); ++i) {
 | 
						|
        QChar c = type.at(i);
 | 
						|
        if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') {
 | 
						|
            accu += c;
 | 
						|
        } else if (c == '<') {
 | 
						|
            ++templateLevel;
 | 
						|
            accu += c;
 | 
						|
        } else if (c == '<') {
 | 
						|
            --templateLevel;
 | 
						|
            accu += c;
 | 
						|
        } else if (templateLevel > 0) {
 | 
						|
            accu += c;
 | 
						|
        } else {
 | 
						|
            if (accu.contains(':') || accu.contains('<'))
 | 
						|
                result += '\'' + accu + '\'';
 | 
						|
            else
 | 
						|
                result += accu;
 | 
						|
            accu.clear();
 | 
						|
            result += c;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (accu.contains(':') || accu.contains('<'))
 | 
						|
        result += '\'' + accu + '\'';
 | 
						|
    else
 | 
						|
        result += accu;
 | 
						|
    //qDebug() << "GDB_QUOTING" << type << " TO " << result;
 | 
						|
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
static void setWatchDataValue(WatchData &data, const GdbMi &mi,
 | 
						|
    int encoding = 0)
 | 
						|
{
 | 
						|
    if (mi.isValid()) {
 | 
						|
        QByteArray ba;
 | 
						|
        switch (encoding) {
 | 
						|
            case 0: // unencoded 8 bit data
 | 
						|
                ba = mi.data();
 | 
						|
                break;
 | 
						|
            case 1: //  base64 encoded 8 bit data
 | 
						|
                ba = QByteArray::fromBase64(mi.data());
 | 
						|
                ba = '"' + ba + '"';
 | 
						|
                break;
 | 
						|
            case 2: //  base64 encoded 16 bit data
 | 
						|
                ba = QByteArray::fromBase64(mi.data());
 | 
						|
                ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8();
 | 
						|
                ba = '"' + ba + '"';
 | 
						|
                break;
 | 
						|
            case 3: //  base64 encoded 32 bit data
 | 
						|
                ba = QByteArray::fromBase64(mi.data());
 | 
						|
                ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8();
 | 
						|
                ba = '"' + ba + '"';
 | 
						|
                break;
 | 
						|
        }
 | 
						|
       data.setValue(ba);
 | 
						|
    } else {
 | 
						|
       data.setValueNeeded();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
 | 
						|
{
 | 
						|
    if (mi.isValid())
 | 
						|
        data.editvalue = mi.data();
 | 
						|
}
 | 
						|
 | 
						|
static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi)
 | 
						|
{
 | 
						|
    if (mi.isValid())
 | 
						|
        data.setValueToolTip(mi.data());
 | 
						|
}
 | 
						|
 | 
						|
static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
 | 
						|
{
 | 
						|
    if (mi.isValid()) {
 | 
						|
        data.childCount = mi.data().toInt();
 | 
						|
        data.setChildCountUnneeded();
 | 
						|
        if (data.childCount == 0)
 | 
						|
            data.setChildrenUnneeded();
 | 
						|
    } else {
 | 
						|
        data.childCount = -1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi)
 | 
						|
{
 | 
						|
    if (mi.data() == "true")
 | 
						|
        data.valuedisabled = true;
 | 
						|
    else if (mi.data() == "false")
 | 
						|
        data.valuedisabled = false;
 | 
						|
}
 | 
						|
 | 
						|
static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
 | 
						|
{
 | 
						|
    if (mi.isValid())
 | 
						|
        data.exp = "(" + mi.data() + ")";
 | 
						|
}
 | 
						|
 | 
						|
static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
 | 
						|
{
 | 
						|
    if (mi.isValid()) {
 | 
						|
        data.addr = mi.data();
 | 
						|
        if (data.exp.isEmpty())
 | 
						|
            data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
 | 
						|
{
 | 
						|
    // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
 | 
						|
    // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
 | 
						|
    // whether parsing was successful
 | 
						|
    int level = 0;
 | 
						|
    bool skipSpace = false;
 | 
						|
    for (int i = 0; i != type.size(); ++i) {
 | 
						|
        QChar c = type[i];
 | 
						|
        if (c == ' ' && skipSpace) {
 | 
						|
            skipSpace = false;
 | 
						|
        } else if (c == '<') {
 | 
						|
            *(level == 0 ? tmplate : inner) += c;
 | 
						|
            ++level;
 | 
						|
        } else if (c == '>') {
 | 
						|
            --level;
 | 
						|
            *(level == 0 ? tmplate : inner) += c;
 | 
						|
        } else if (c == ',') {
 | 
						|
            *inner += (level == 1) ? '@' : ',';
 | 
						|
            skipSpace = true;
 | 
						|
        } else {
 | 
						|
            *(level == 0 ? tmplate : inner) += c;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    *tmplate = tmplate->trimmed();
 | 
						|
    *tmplate = tmplate->remove("<>");
 | 
						|
    *inner = inner->trimmed();
 | 
						|
    //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner << " FROM " << type;
 | 
						|
    return !inner->isEmpty();
 | 
						|
}
 | 
						|
 | 
						|
static QString extractTypeFromPTypeOutput(const QString &str)
 | 
						|
{
 | 
						|
    int pos0 = str.indexOf('=');
 | 
						|
    int pos1 = str.indexOf('{');
 | 
						|
    int pos2 = str.lastIndexOf('}');
 | 
						|
    QString res = str;
 | 
						|
    if (pos0 != -1 && pos1 != -1 && pos2 != -1)
 | 
						|
        res = str.mid(pos0 + 2, pos1 - 1 - pos0)
 | 
						|
            + " ... " + str.right(str.size() - pos2);
 | 
						|
    return res.simplified();
 | 
						|
}
 | 
						|
 | 
						|
static bool isIntOrFloatType(const QString &type)
 | 
						|
{
 | 
						|
    static const QStringList types = QStringList()
 | 
						|
        << "char" << "int" << "short" << "float" << "double" << "long"
 | 
						|
        << "bool" << "signed char" << "unsigned" << "unsigned char"
 | 
						|
        << "unsigned int" << "unsigned long" << "long long"
 | 
						|
        << "unsigned long long";
 | 
						|
    return types.contains(type);
 | 
						|
}
 | 
						|
 | 
						|
static QString sizeofTypeExpression(const QString &type)
 | 
						|
{
 | 
						|
    if (type.endsWith('*'))
 | 
						|
        return "sizeof(void*)";
 | 
						|
    if (type.endsWith('>'))
 | 
						|
        return "sizeof(" + type + ")";
 | 
						|
    return "sizeof(" + gdbQuoteTypes(type) + ")";
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::setUseCustomDumpers(bool on)
 | 
						|
{
 | 
						|
    //qDebug() << "SWITCHING ON/OFF DUMPER DEBUGGING:" << on;
 | 
						|
    Q_UNUSED(on);
 | 
						|
    // FIXME: a bit too harsh, but otherwise the treeview sometimes look funny
 | 
						|
    //m_expandedINames.clear();
 | 
						|
    updateLocals();
 | 
						|
}
 | 
						|
 | 
						|
bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
 | 
						|
{
 | 
						|
    DebuggerSettings *s = q->settings();
 | 
						|
    if (!s->m_useCustomDumpers)
 | 
						|
        return false;
 | 
						|
    if (s->m_debugDumpers && qq->stackHandler()->isDebuggingDumpers())
 | 
						|
        return false;
 | 
						|
    if (m_dataDumperState != DataDumperAvailable)
 | 
						|
        return false;
 | 
						|
 | 
						|
    // simple types
 | 
						|
    if (m_availableSimpleDumpers.contains(type))
 | 
						|
        return true;
 | 
						|
 | 
						|
    // templates
 | 
						|
    QString tmplate;
 | 
						|
    QString inner;
 | 
						|
    if (!extractTemplate(type, &tmplate, &inner))
 | 
						|
        return false;
 | 
						|
    return m_availableSimpleDumpers.contains(tmplate);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
 | 
						|
{
 | 
						|
    WatchData data = data0;
 | 
						|
    QTC_ASSERT(!data.exp.isEmpty(), return);
 | 
						|
    QString tmplate;
 | 
						|
    QString inner;
 | 
						|
    bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
 | 
						|
    QStringList inners = inner.split('@');
 | 
						|
    if (inners.at(0).isEmpty())
 | 
						|
        inners.clear();
 | 
						|
    for (int i = 0; i != inners.size(); ++i)
 | 
						|
        inners[i] = inners[i].simplified();
 | 
						|
 | 
						|
    QString outertype = isTemplate ? tmplate : data.type;
 | 
						|
    // adjust the data extract
 | 
						|
    if (outertype == m_namespace + "QWidget")
 | 
						|
        outertype = m_namespace + "QObject";
 | 
						|
 | 
						|
    QString extraArgs[4];
 | 
						|
    extraArgs[0] = "0";
 | 
						|
    extraArgs[1] = "0";
 | 
						|
    extraArgs[2] = "0";
 | 
						|
    extraArgs[3] = "0";
 | 
						|
    int extraArgCount = 0;
 | 
						|
 | 
						|
    // "generic" template dumpers: passing sizeof(argument)
 | 
						|
    // gives already most information the dumpers need
 | 
						|
    foreach (const QString &arg, inners)
 | 
						|
        extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
 | 
						|
 | 
						|
    // in rare cases we need more or less:
 | 
						|
    if (outertype == m_namespace + "QObject") {
 | 
						|
        extraArgs[0] = "(char*)&((('"
 | 
						|
            + m_namespace + "QObjectPrivate'*)&"
 | 
						|
            + data.exp + ")->children)-(char*)&" + data.exp;
 | 
						|
    } else if (outertype == m_namespace + "QVector") {
 | 
						|
        extraArgs[1] = "(char*)&(("
 | 
						|
            + data.exp + ").d->array)-(char*)" + data.exp + ".d";
 | 
						|
    } else if (outertype == m_namespace + "QObjectSlot"
 | 
						|
            || outertype == m_namespace + "QObjectSignal") {
 | 
						|
        // we need the number out of something like
 | 
						|
        // iname="local.ob.slots.[2]deleteLater()"
 | 
						|
        int lastOpened = data.iname.lastIndexOf('[');
 | 
						|
        int lastClosed = data.iname.lastIndexOf(']');
 | 
						|
        QString slotNumber = "-1";
 | 
						|
        if (lastOpened != -1 && lastClosed != -1)
 | 
						|
            slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1);
 | 
						|
        extraArgs[0] = slotNumber;
 | 
						|
    } else if (outertype == m_namespace + "QMap" || outertype == m_namespace + "QMultiMap") {
 | 
						|
        QString nodetype;
 | 
						|
        if (m_qtVersion >= (4 << 16) + (5 << 8) + 0) {
 | 
						|
            nodetype  = m_namespace + "QMapNode";
 | 
						|
            nodetype += data.type.mid(outertype.size());
 | 
						|
        } else {
 | 
						|
            // FIXME: doesn't work for QMultiMap
 | 
						|
            nodetype  = data.type + "::Node"; 
 | 
						|
        }
 | 
						|
        //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype
 | 
						|
        //    << "QT VERSION" << m_qtVersion << ((4 << 16) + (5 << 8) + 0);
 | 
						|
        extraArgs[2] = sizeofTypeExpression(nodetype);
 | 
						|
        extraArgs[3] = "(size_t)&(('" + nodetype + "'*)0)->value";
 | 
						|
    } else if (outertype == m_namespace + "QMapNode") {
 | 
						|
        extraArgs[2] = sizeofTypeExpression(data.type);
 | 
						|
        extraArgs[3] = "(size_t)&(('" + data.type + "'*)0)->value";
 | 
						|
    } else if (outertype == "std::vector") {
 | 
						|
        //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
 | 
						|
        if (inners.at(0) == "bool") {
 | 
						|
            outertype = "std::vector::bool";
 | 
						|
        } else {
 | 
						|
            //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
 | 
						|
            //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
 | 
						|
        }
 | 
						|
    } else if (outertype == "std::deque") {
 | 
						|
        // remove 'std::allocator<...>':
 | 
						|
        extraArgs[1] = "0";
 | 
						|
    } else if (outertype == "std::stack") {
 | 
						|
        // remove 'std::allocator<...>':
 | 
						|
        extraArgs[1] = "0";
 | 
						|
    } else if (outertype == "std::map") {
 | 
						|
        // We don't want the comparator and the allocator confuse gdb.
 | 
						|
        // But we need the offset of the second item in the value pair.
 | 
						|
        // We read the type of the pair from the allocator argument because
 | 
						|
        // that gets the constness "right" (in the sense that gdb can
 | 
						|
        // read it back;
 | 
						|
        QString pairType = inners.at(3);
 | 
						|
        // remove 'std::allocator<...>':
 | 
						|
        pairType = pairType.mid(15, pairType.size() - 15 - 2);
 | 
						|
        extraArgs[2] = "(size_t)&(('" + pairType + "'*)0)->second";
 | 
						|
        extraArgs[3] = "0";
 | 
						|
    } else if (outertype == "std::basic_string") {
 | 
						|
        //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
 | 
						|
        if (inners.at(0) == "char") {
 | 
						|
            outertype = "std::string";
 | 
						|
        } else if (inners.at(0) == "wchar_t") {
 | 
						|
            outertype = "std::wstring";
 | 
						|
        }
 | 
						|
        extraArgs[0] = "0";
 | 
						|
        extraArgs[1] = "0";
 | 
						|
        extraArgs[2] = "0";
 | 
						|
        extraArgs[3] = "0";
 | 
						|
    }
 | 
						|
 | 
						|
    //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
 | 
						|
    //int protocol = data.iname.startsWith("watch") ? 3 : 2;
 | 
						|
    int protocol = 2;
 | 
						|
    //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
 | 
						|
 | 
						|
    QString addr;
 | 
						|
    if (data.addr.startsWith("0x"))
 | 
						|
        addr = "(void*)" + data.addr;
 | 
						|
    else
 | 
						|
        addr = "&(" + data.exp + ")";
 | 
						|
 | 
						|
    QByteArray params;
 | 
						|
    params.append(outertype);
 | 
						|
    params.append('\0');
 | 
						|
    params.append(data.iname);
 | 
						|
    params.append('\0');
 | 
						|
    params.append(data.exp);
 | 
						|
    params.append('\0');
 | 
						|
    params.append(inner);
 | 
						|
    params.append('\0');
 | 
						|
    params.append(data.iname);
 | 
						|
    params.append('\0');
 | 
						|
 | 
						|
    sendWatchParameters(params);
 | 
						|
 | 
						|
    QString cmd ="call "
 | 
						|
            + QString("qDumpObjectData440(")
 | 
						|
            + QString::number(protocol)
 | 
						|
            + ',' + "%1+1"                // placeholder for token
 | 
						|
            + ',' + addr
 | 
						|
            + ',' + (dumpChildren ? "1" : "0")
 | 
						|
            + ',' + extraArgs[0]
 | 
						|
            + ',' + extraArgs[1]
 | 
						|
            + ',' + extraArgs[2]
 | 
						|
            + ',' + extraArgs[3] + ')';
 | 
						|
 | 
						|
    //qDebug() << "CMD: " << cmd;
 | 
						|
 | 
						|
    QVariant var;
 | 
						|
    var.setValue(data);
 | 
						|
    sendSynchronizedCommand(cmd, WatchDumpCustomValue1, var);
 | 
						|
 | 
						|
    q->showStatusMessage(
 | 
						|
        tr("Retrieving data for watch view (%1 requests pending)...")
 | 
						|
            .arg(m_pendingRequests + 1), 10000);
 | 
						|
 | 
						|
    // retrieve response
 | 
						|
    sendSynchronizedCommand("p (char*)qDumpOutBuffer", WatchDumpCustomValue2, var);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::createGdbVariable(const WatchData &data)
 | 
						|
{
 | 
						|
    sendSynchronizedCommand("-var-delete \"" + data.iname + '"');
 | 
						|
    QString exp = data.exp;
 | 
						|
    if (exp.isEmpty() && data.addr.startsWith("0x"))
 | 
						|
        exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr;
 | 
						|
    QVariant val = QVariant::fromValue<WatchData>(data);
 | 
						|
    sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * "
 | 
						|
        + '"' + exp + '"', WatchVarCreate, val);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::updateSubItem(const WatchData &data0)
 | 
						|
{
 | 
						|
    WatchData data = data0;
 | 
						|
    #if DEBUG_SUBITEM
 | 
						|
    qDebug() << "UPDATE SUBITEM: " << data.toString();
 | 
						|
    #endif
 | 
						|
    QTC_ASSERT(data.isValid(), return);
 | 
						|
 | 
						|
    // in any case we need the type first
 | 
						|
    if (data.isTypeNeeded()) {
 | 
						|
        // This should only happen if we don't have a variable yet.
 | 
						|
        // Let's play safe, though.
 | 
						|
        if (!data.variable.isEmpty()) {
 | 
						|
            // Update: It does so for out-of-scope watchers.
 | 
						|
            #if 1
 | 
						|
            qDebug() << "FIXME: GdbEngine::updateSubItem: "
 | 
						|
                 << data.toString() << "should not happen";
 | 
						|
            #else
 | 
						|
            data.setType("<out of scope>");
 | 
						|
            data.setValue("<out of scope>");
 | 
						|
            data.setChildCount(0);
 | 
						|
            insertData(data);
 | 
						|
            return;
 | 
						|
            #endif
 | 
						|
        }
 | 
						|
        // The WatchVarCreate handler will receive type information
 | 
						|
        // and re-insert a WatchData item with correct type, so
 | 
						|
        // we will not re-enter this bit.
 | 
						|
        // FIXME: Concurrency issues?
 | 
						|
        createGdbVariable(data);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // we should have a type now. this is relied upon further below
 | 
						|
    QTC_ASSERT(!data.type.isEmpty(), return);
 | 
						|
 | 
						|
    // a common case that can be easily solved
 | 
						|
    if (data.isChildrenNeeded() && isPointerType(data.type)
 | 
						|
        && !isCustomValueDumperAvailable(data.type)) {
 | 
						|
        // We sometimes know what kind of children pointers have
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "IT'S A POINTER";
 | 
						|
        #endif
 | 
						|
#if 1
 | 
						|
        WatchData data1;
 | 
						|
        data1.iname = data.iname + ".*";
 | 
						|
        data1.name = "*" + data.name;
 | 
						|
        data1.exp = "(*(" + data.exp + "))";
 | 
						|
        data1.type = stripPointerType(data.type);
 | 
						|
        data1.setValueNeeded();
 | 
						|
        insertData(data1);
 | 
						|
        data.setChildrenUnneeded();
 | 
						|
        insertData(data);
 | 
						|
#else
 | 
						|
        // Try automatic dereferentiation
 | 
						|
        data.exp = "*(" + data.exp + ")";
 | 
						|
        data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
 | 
						|
        insertData(data);
 | 
						|
#endif
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) {
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
 | 
						|
        #endif
 | 
						|
        runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
/*
 | 
						|
    if (data.isValueNeeded() && data.exp.isEmpty()) {
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
 | 
						|
        #endif
 | 
						|
        data.setError("<no expression given>");
 | 
						|
        insertData(data);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
*/
 | 
						|
 | 
						|
    if (data.isValueNeeded() && data.variable.isEmpty()) {
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
 | 
						|
        #endif
 | 
						|
        createGdbVariable(data);
 | 
						|
        // the WatchVarCreate handler will re-insert a WatchData
 | 
						|
        // item, with valueNeeded() set.
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isValueNeeded()) {
 | 
						|
        QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: VALUE";
 | 
						|
        #endif
 | 
						|
        QString cmd = "-var-evaluate-expression \"" + data.iname + "\"";
 | 
						|
        sendSynchronizedCommand(cmd, WatchEvaluateExpression,
 | 
						|
            QVariant::fromValue(data));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) {
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
 | 
						|
        #endif
 | 
						|
        runCustomDumper(data, true);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isChildrenNeeded() && data.variable.isEmpty()) {
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
 | 
						|
        #endif
 | 
						|
        createGdbVariable(data);
 | 
						|
        // the WatchVarCreate handler will re-insert a WatchData
 | 
						|
        // item, with childrenNeeded() set.
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isChildrenNeeded()) {
 | 
						|
        QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
 | 
						|
        QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
 | 
						|
        sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) {
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
 | 
						|
        #endif
 | 
						|
        runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isChildCountNeeded() && data.variable.isEmpty()) {
 | 
						|
        #if DEBUG_SUBITEM
 | 
						|
        qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
 | 
						|
        #endif
 | 
						|
        createGdbVariable(data);
 | 
						|
        // the WatchVarCreate handler will re-insert a WatchData
 | 
						|
        // item, with childrenNeeded() set.
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (data.isChildCountNeeded()) {
 | 
						|
        QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
 | 
						|
        QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
 | 
						|
        sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString();
 | 
						|
    QTC_ASSERT(false, return);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::updateWatchModel()
 | 
						|
{
 | 
						|
    m_pendingRequests = 0;
 | 
						|
    PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL");
 | 
						|
    updateWatchModel2();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::updateWatchModel2()
 | 
						|
{
 | 
						|
    PENDING_DEBUG("UPDATE WATCH MODEL");
 | 
						|
    QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes();
 | 
						|
    //QTC_ASSERT(incomplete.isEmpty(), /**/);
 | 
						|
    if (!incomplete.isEmpty()) {
 | 
						|
        #if DEBUG_PENDING
 | 
						|
        qDebug() << "##############################################";
 | 
						|
        qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:";
 | 
						|
        foreach (const WatchData &data, incomplete)
 | 
						|
            qDebug() << data.toString();
 | 
						|
        #endif
 | 
						|
 | 
						|
        // Bump requests to avoid model rebuilding during the nested
 | 
						|
        // updateWatchModel runs.
 | 
						|
        ++m_pendingRequests;
 | 
						|
        foreach (const WatchData &data, incomplete)
 | 
						|
            updateSubItem(data);
 | 
						|
        PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
 | 
						|
        updateWatchModel2();
 | 
						|
        --m_pendingRequests;
 | 
						|
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_pendingRequests > 0) {
 | 
						|
        PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    PENDING_DEBUG("REBUILDING MODEL");
 | 
						|
    emit gdbInputAvailable(QString(),
 | 
						|
        "[" + currentTime() + "]    <Rebuild Watchmodel>");
 | 
						|
    q->showStatusMessage(tr("Finished retrieving data."), 400);
 | 
						|
    qq->watchHandler()->rebuildModel();
 | 
						|
 | 
						|
    if (!m_toolTipExpression.isEmpty()) {
 | 
						|
        WatchData *data = qq->watchHandler()->findData(tooltipIName);
 | 
						|
        if (data) {
 | 
						|
            //m_toolTipCache[data->exp] = *data;
 | 
						|
            QToolTip::showText(m_toolTipPos,
 | 
						|
                    "(" + data->type + ") " + data->exp + " = " + data->value);
 | 
						|
        } else {
 | 
						|
            QToolTip::showText(m_toolTipPos,
 | 
						|
                "Cannot evaluate expression: " + m_toolTipExpression);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    Q_UNUSED(record);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
 | 
						|
    GdbMi output = record.data.findChild("consolestreamoutput");
 | 
						|
    QByteArray out = output.data();
 | 
						|
    out = out.mid(out.indexOf('"') + 2); // + 1 is success marker
 | 
						|
    out = out.left(out.lastIndexOf('"'));
 | 
						|
    //out.replace('\'', '"');
 | 
						|
    out.replace("\\", "");
 | 
						|
    out = "dummy={" + out + "}";
 | 
						|
    //qDebug() << "OUTPUT: " << out;
 | 
						|
 | 
						|
    GdbMi contents;
 | 
						|
    contents.fromString(out);
 | 
						|
    GdbMi simple = contents.findChild("dumpers");
 | 
						|
    m_namespace = contents.findChild("namespace").data();
 | 
						|
    GdbMi qtversion = contents.findChild("qtversion");
 | 
						|
    if (qtversion.children().size() == 3) {
 | 
						|
        m_qtVersion = (qtversion.childAt(0).data().toInt() << 16)
 | 
						|
                    + (qtversion.childAt(1).data().toInt() << 8)
 | 
						|
                    + qtversion.childAt(2).data().toInt();
 | 
						|
        //qDebug() << "FOUND QT VERSION: " << qtversion.toString() << m_qtVersion;
 | 
						|
    } else {
 | 
						|
        m_qtVersion = 0;
 | 
						|
    }
 | 
						|
   
 | 
						|
    //qDebug() << "CONTENTS: " << contents.toString();
 | 
						|
    //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
 | 
						|
    m_availableSimpleDumpers.clear();
 | 
						|
    foreach (const GdbMi &item, simple.children())
 | 
						|
        m_availableSimpleDumpers.append(item.data());
 | 
						|
    if (m_availableSimpleDumpers.isEmpty()) {
 | 
						|
        m_dataDumperState = DataDumperUnavailable;
 | 
						|
        QMessageBox::warning(q->mainWindow(),
 | 
						|
            tr("Cannot find special data dumpers"),
 | 
						|
            tr("The debugged binary does not contain information needed for "
 | 
						|
                    "nice display of Qt data types.\n\n"
 | 
						|
                    "Try might want to try include the file\n\n"
 | 
						|
                    ".../ide/main/bin/gdbmacros/gdbmacros.cpp'\n\n"
 | 
						|
                    "into your project directly.")
 | 
						|
                );
 | 
						|
    } else {
 | 
						|
        m_dataDumperState = DataDumperAvailable;
 | 
						|
    }
 | 
						|
    //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::sendWatchParameters(const QByteArray ¶ms0)
 | 
						|
{
 | 
						|
    QByteArray params = params0;
 | 
						|
    params.append('\0');
 | 
						|
    char buf[50];
 | 
						|
    sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size());
 | 
						|
    QByteArray encoded;
 | 
						|
    encoded.append(buf);
 | 
						|
    for (int i = 0; i != params.size(); ++i) {
 | 
						|
        sprintf(buf, "%d,", int(params[i]));
 | 
						|
        encoded.append(buf);
 | 
						|
    }
 | 
						|
    encoded[encoded.size() - 1] = '}';
 | 
						|
 | 
						|
    sendCommand(encoded);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleVarAssign()
 | 
						|
{
 | 
						|
    // everything might have changed, force re-evaluation
 | 
						|
    // FIXME: Speed this up by re-using variables and only
 | 
						|
    // marking values as 'unknown'
 | 
						|
    updateLocals();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi)
 | 
						|
{
 | 
						|
    if (mi.isValid()) {
 | 
						|
        if (!data.framekey.isEmpty())
 | 
						|
            m_varToType[data.framekey] = mi.data();
 | 
						|
        data.setType(mi.data());
 | 
						|
    } else if (data.type.isEmpty()) {
 | 
						|
        data.setTypeNeeded();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleVarCreate(const GdbResultRecord &record,
 | 
						|
    const WatchData &data0)
 | 
						|
{
 | 
						|
    WatchData data = data0;
 | 
						|
    // happens e.g. when we already issued a var-evaluate command
 | 
						|
    if (!data.isValid())
 | 
						|
        return;
 | 
						|
    //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString();
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        data.variable = data.iname;
 | 
						|
        setWatchDataType(data, record.data.findChild("type"));
 | 
						|
        if (isCustomValueDumperAvailable(data.type)) {
 | 
						|
            // we do not trust gdb if we have a custom dumper
 | 
						|
            if (record.data.findChild("children").isValid())
 | 
						|
                data.setChildrenUnneeded();
 | 
						|
            else if (qq->watchHandler()->isExpandedIName(data.iname))
 | 
						|
                data.setChildrenNeeded();
 | 
						|
            insertData(data);
 | 
						|
        } else {
 | 
						|
            if (record.data.findChild("children").isValid())
 | 
						|
                data.setChildrenUnneeded();
 | 
						|
            else if (qq->watchHandler()->isExpandedIName(data.iname))
 | 
						|
                data.setChildrenNeeded();
 | 
						|
            setWatchDataChildCount(data, record.data.findChild("numchild"));
 | 
						|
            //if (data.isValueNeeded() && data.childCount > 0)
 | 
						|
            //    data.setValue(QByteArray());
 | 
						|
            insertData(data);
 | 
						|
        }
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        data.setError(record.data.findChild("msg").data());
 | 
						|
        if (data.isWatcher()) {
 | 
						|
            data.value = strNotInScope;
 | 
						|
            data.type = " ";
 | 
						|
            data.setAllUnneeded();
 | 
						|
            data.setChildCount(0);
 | 
						|
            data.valuedisabled = true;
 | 
						|
            insertData(data);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record,
 | 
						|
    const WatchData &data0)
 | 
						|
{
 | 
						|
    WatchData data = data0;
 | 
						|
    QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        //if (col == 0)
 | 
						|
        //    data.name = record.data.findChild("value").data();
 | 
						|
        //else
 | 
						|
            setWatchDataValue(data, record.data.findChild("value"));
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        data.setError(record.data.findChild("msg").data());
 | 
						|
    }
 | 
						|
    //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
 | 
						|
    insertData(data);
 | 
						|
    //updateWatchModel2();
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
    qDebug() << "CUSTOM SETUP RESULT: " << record.toString();
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        QString msg = record.data.findChild("msg").data();
 | 
						|
        qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record,
 | 
						|
    const WatchData &data0)
 | 
						|
{
 | 
						|
    WatchData data = data0;
 | 
						|
    QTC_ASSERT(data.isValid(), return);
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        // ignore this case, data will follow
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        // Record an extra result, as the socket result will be lost
 | 
						|
        // in transmission
 | 
						|
        //--m_pendingRequests;
 | 
						|
        QString msg = record.data.findChild("msg").data();
 | 
						|
        //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
 | 
						|
#ifdef QT_DEBUG
 | 
						|
        // Make debugging of dumpers easier
 | 
						|
        if (q->settings()->m_debugDumpers
 | 
						|
                && msg.startsWith("The program being debugged stopped while")
 | 
						|
                && msg.contains("qDumpObjectData440")) {
 | 
						|
            // Fake full stop
 | 
						|
            sendCommand("-file-list-exec-source-files", GdbQuerySources);
 | 
						|
            sendCommand("-break-list", BreakList);
 | 
						|
            sendCommand("p 0", GdbAsyncOutput2);  // dummy
 | 
						|
            return;
 | 
						|
        }
 | 
						|
#endif
 | 
						|
        //if (msg.startsWith("The program being debugged was sig"))
 | 
						|
        //    msg = strNotInScope;
 | 
						|
        //if (msg.startsWith("The program being debugged stopped while"))
 | 
						|
        //    msg = strNotInScope;
 | 
						|
        //data.setError(msg);
 | 
						|
        //insertData(data);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
 | 
						|
    const WatchData &data0)
 | 
						|
{
 | 
						|
    WatchData data = data0;
 | 
						|
    QTC_ASSERT(data.isValid(), return);
 | 
						|
    //qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
 | 
						|
    //qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
 | 
						|
    if (record.resultClass != GdbResultDone) {
 | 
						|
        qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    GdbMi output = record.data.findChild("consolestreamoutput");
 | 
						|
    QByteArray out = output.data();
 | 
						|
 | 
						|
    int markerPos = out.indexOf('"') + 1; // position of 'success marker'
 | 
						|
    if (markerPos == -1 || out.at(markerPos) == 'f') {  // 't' or 'f'
 | 
						|
        // custom dumper produced no output
 | 
						|
        data.setError(strNotInScope);
 | 
						|
        insertData(data);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    out = out.mid(markerPos +  1);
 | 
						|
    out = out.left(out.lastIndexOf('"'));
 | 
						|
    out.replace("\\", "");
 | 
						|
    out = "dummy={" + out + "}";
 | 
						|
    
 | 
						|
    GdbMi contents;
 | 
						|
    contents.fromString(out);
 | 
						|
    //qDebug() << "CONTENTS" << contents.toString(true);
 | 
						|
    if (!contents.isValid()) {
 | 
						|
        data.setError(strNotInScope);
 | 
						|
        insertData(data);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    setWatchDataType(data, contents.findChild("type"));
 | 
						|
    setWatchDataValue(data, contents.findChild("value"),
 | 
						|
        contents.findChild("valueencoded").data().toInt());
 | 
						|
    setWatchDataAddress(data, contents.findChild("addr"));
 | 
						|
    setWatchDataChildCount(data, contents.findChild("numchild"));
 | 
						|
    setWatchDataValueToolTip(data, contents.findChild("valuetooltip"));
 | 
						|
    setWatchDataValueDisabled(data, contents.findChild("valuedisabled"));
 | 
						|
    setWatchDataEditValue(data, contents.findChild("editvalue"));
 | 
						|
    if (qq->watchHandler()->isDisplayedIName(data.iname)) {
 | 
						|
        GdbMi editvalue = contents.findChild("editvalue");
 | 
						|
        if (editvalue.isValid()) {
 | 
						|
            setWatchDataEditValue(data, editvalue);
 | 
						|
            qq->watchHandler()->showEditValue(data);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (!qq->watchHandler()->isExpandedIName(data.iname))
 | 
						|
        data.setChildrenUnneeded();
 | 
						|
    GdbMi children = contents.findChild("children");
 | 
						|
    if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname))
 | 
						|
        data.setChildrenUnneeded();
 | 
						|
    data.setValueUnneeded();
 | 
						|
 | 
						|
    // try not to repeat data too often
 | 
						|
    WatchData childtemplate;
 | 
						|
    setWatchDataType(childtemplate, contents.findChild("childtype"));
 | 
						|
    setWatchDataChildCount(childtemplate, contents.findChild("childnumchild"));
 | 
						|
    //qDebug() << "DATA: " << data.toString();
 | 
						|
    insertData(data);
 | 
						|
    foreach (GdbMi item, children.children()) {
 | 
						|
        WatchData data1 = childtemplate;
 | 
						|
        data1.name = item.findChild("name").data();
 | 
						|
        data1.iname = data.iname + "." + data1.name;
 | 
						|
        if (!data1.name.isEmpty() && data1.name.at(0).isDigit())
 | 
						|
            data1.name = '[' + data1.name + ']';
 | 
						|
        QString key = item.findChild("key").data();
 | 
						|
        if (!key.isEmpty()) {
 | 
						|
            if (item.findChild("keyencoded").data()[0] == '1') {
 | 
						|
                key = '"' + QByteArray::fromBase64(key.toUtf8()) + '"';
 | 
						|
                if (key.size() > 13) {
 | 
						|
                    key = key.left(12);
 | 
						|
                    key += "...";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            //data1.name += " (" + key + ")";
 | 
						|
            data1.name = key;
 | 
						|
        }
 | 
						|
        setWatchDataType(data1, item.findChild("type"));
 | 
						|
        setWatchDataExpression(data1, item.findChild("exp"));
 | 
						|
        setWatchDataChildCount(data1, item.findChild("numchild"));
 | 
						|
        setWatchDataValue(data1, item.findChild("value"),
 | 
						|
            item.findChild("valueencoded").data().toInt());
 | 
						|
        setWatchDataAddress(data1, item.findChild("addr"));
 | 
						|
        setWatchDataValueToolTip(data1, item.findChild("valuetooltip"));
 | 
						|
        setWatchDataValueDisabled(data1, item.findChild("valuedisabled"));
 | 
						|
        if (!qq->watchHandler()->isExpandedIName(data1.iname))
 | 
						|
            data1.setChildrenUnneeded();
 | 
						|
        //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
 | 
						|
        insertData(data1);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::updateLocals()
 | 
						|
{
 | 
						|
setTokenBarrier();
 | 
						|
 | 
						|
m_pendingRequests = 0;
 | 
						|
PENDING_DEBUG("\nRESET PENDING");
 | 
						|
m_toolTipCache.clear();
 | 
						|
m_toolTipExpression.clear();
 | 
						|
qq->watchHandler()->reinitializeWatchers();
 | 
						|
 | 
						|
int level = currentFrame();
 | 
						|
// '2' is 'list with type and value'
 | 
						|
QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level);
 | 
						|
sendSynchronizedCommand(cmd, StackListArguments);                 // stage 1/2
 | 
						|
// '2' is 'list with type and value'
 | 
						|
sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleStackListArguments(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
// stage 1/2
 | 
						|
 | 
						|
// Linux:
 | 
						|
// 12^done,stack-args=
 | 
						|
//   [frame={level="0",args=[
 | 
						|
//     {name="argc",type="int",value="1"},
 | 
						|
//     {name="argv",type="char **",value="(char **) 0x7..."}]}]
 | 
						|
// Mac:
 | 
						|
// 78^done,stack-args=
 | 
						|
//    {frame={level="0",args={
 | 
						|
//      varobj=
 | 
						|
//        {exp="this",value="0x38a2fab0",name="var21",numchild="3",
 | 
						|
//             type="CurrentDocumentFind *  const",typecode="PTR",
 | 
						|
//             dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
 | 
						|
//             block_end_addr="0x3938eb2d"},
 | 
						|
//      varobj=
 | 
						|
//         {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
 | 
						|
//              name="var22",numchild="1",type="const QString  ...} }}}
 | 
						|
//
 | 
						|
// In both cases, iterating over the children of stack-args/frame/args
 | 
						|
// is ok.
 | 
						|
m_currentFunctionArgs.clear();
 | 
						|
if (record.resultClass == GdbResultDone) {
 | 
						|
    const GdbMi list = record.data.findChild("stack-args");
 | 
						|
    const GdbMi frame = list.findChild("frame");
 | 
						|
    const GdbMi args = frame.findChild("args");
 | 
						|
    m_currentFunctionArgs = args.children();
 | 
						|
} else if (record.resultClass == GdbResultError) {
 | 
						|
    qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen";
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleStackListLocals(const GdbResultRecord &record)
 | 
						|
{
 | 
						|
// stage 2/2
 | 
						|
 | 
						|
// There could be shadowed variables
 | 
						|
QList<GdbMi> locals = record.data.findChild("locals").children();
 | 
						|
locals += m_currentFunctionArgs;
 | 
						|
 | 
						|
setLocals(locals);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::setLocals(const QList<GdbMi> &locals) 
 | 
						|
{ 
 | 
						|
//qDebug() << m_varToType;
 | 
						|
QHash<QString, int> seen;
 | 
						|
 | 
						|
foreach (const GdbMi &item, locals) {
 | 
						|
    // Local variables of inlined code are reported as 
 | 
						|
    // 26^done,locals={varobj={exp="this",value="",name="var4",exp="this",
 | 
						|
    // numchild="1",type="const QtSharedPointer::Basic<CPlusPlus::..."
 | 
						|
    // We do not want these at all. Current hypotheses is that those
 | 
						|
    // "spurious" locals have _two_ "exp" field. Try to filter them:
 | 
						|
    #ifdef Q_OS_MAC
 | 
						|
    int numExps = 0;
 | 
						|
    foreach (const GdbMi &child, item.children())
 | 
						|
        numExps += int(child.name() == "exp");
 | 
						|
    if (numExps > 1)
 | 
						|
        continue;
 | 
						|
    QString name = item.findChild("exp").data();
 | 
						|
    #else
 | 
						|
    QString name = item.findChild("name").data();
 | 
						|
    #endif
 | 
						|
    int n = seen.value(name);
 | 
						|
    if (n) {
 | 
						|
        seen[name] = n + 1;
 | 
						|
        WatchData data;
 | 
						|
        data.iname = "local." + name + QString::number(n + 1);
 | 
						|
        data.name = name + QString(" <shadowed %1>").arg(n);
 | 
						|
        //data.setValue("<shadowed>");
 | 
						|
        setWatchDataValue(data, item.findChild("value"));
 | 
						|
        data.setType("<shadowed>");
 | 
						|
        data.setChildCount(0);
 | 
						|
        insertData(data);
 | 
						|
    } else {
 | 
						|
        seen[name] = 1;
 | 
						|
        WatchData data;
 | 
						|
        data.iname = "local." + name;
 | 
						|
        data.name = name;
 | 
						|
        data.exp = name;
 | 
						|
        data.framekey = m_currentFrame + data.name;
 | 
						|
        setWatchDataType(data, item.findChild("type"));
 | 
						|
        // set value only directly if it is simple enough, otherwise
 | 
						|
        // pass through the insertData() machinery
 | 
						|
        if (isIntOrFloatType(data.type) || isPointerType(data.type))
 | 
						|
            setWatchDataValue(data, item.findChild("value"));
 | 
						|
        if (!qq->watchHandler()->isExpandedIName(data.iname))
 | 
						|
            data.setChildrenUnneeded();
 | 
						|
        if (isPointerType(data.type) || data.name == "this")
 | 
						|
            data.setChildCount(1);
 | 
						|
        if (0 && m_varToType.contains(data.framekey)) {
 | 
						|
            qDebug() << "RE-USING " << m_varToType.value(data.framekey);
 | 
						|
            data.setType(m_varToType.value(data.framekey));
 | 
						|
        }
 | 
						|
        insertData(data);
 | 
						|
    }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::insertData(const WatchData &data0)
 | 
						|
{
 | 
						|
    //qDebug() << "INSERT DATA" << data0.toString();
 | 
						|
    WatchData data = data0;
 | 
						|
    if (data.value.startsWith("mi_cmd_var_create:")) {
 | 
						|
        qDebug() << "BOGUS VALUE: " << data.toString();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    qq->watchHandler()->insertData(data);
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleTypeContents(const QString &output)
 | 
						|
{
 | 
						|
    // output.startsWith("type = ") == true
 | 
						|
    // "type = int"
 | 
						|
    // "type = class QString {"
 | 
						|
    // "type = class QStringList : public QList<QString> {"
 | 
						|
    QString tip;
 | 
						|
    QString className;
 | 
						|
    if (output.startsWith("type = class")) {
 | 
						|
        int posBrace = output.indexOf('{');
 | 
						|
        QString head = output.mid(13, posBrace - 13 - 1);
 | 
						|
        int posColon = head.indexOf(": public");
 | 
						|
        if (posColon == -1)
 | 
						|
            posColon = head.indexOf(": protected");
 | 
						|
        if (posColon == -1)
 | 
						|
            posColon = head.indexOf(": private");
 | 
						|
        if (posColon == -1) {
 | 
						|
            className = head;
 | 
						|
            tip = "class " + className + " { ... }";
 | 
						|
        } else {
 | 
						|
            className = head.left(posColon - 1);
 | 
						|
            tip = "class " + head + " { ... }";
 | 
						|
        }
 | 
						|
        //qDebug() << "posColon: " << posColon;
 | 
						|
        //qDebug() << "posBrace: " << posBrace;
 | 
						|
        //qDebug() << "head: " << head;
 | 
						|
    } else {
 | 
						|
        className = output.mid(7);
 | 
						|
        tip = className;
 | 
						|
    }
 | 
						|
    //qDebug() << "output: " << output.left(100) + "...";
 | 
						|
    //qDebug() << "className: " << className;
 | 
						|
    //qDebug() << "tip: " << tip;
 | 
						|
    //m_toolTip.type = className;
 | 
						|
    m_toolTip.type.clear();
 | 
						|
    m_toolTip.value = tip;
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
 | 
						|
    const WatchData &parent)
 | 
						|
{
 | 
						|
    //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 2" << parent.toString();
 | 
						|
    //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
 | 
						|
    QByteArray exp = item.findChild("exp").data();
 | 
						|
    QByteArray name = item.findChild("name").data();
 | 
						|
    if (isAccessSpecifier(exp)) {
 | 
						|
        // suppress 'private'/'protected'/'public' level
 | 
						|
        WatchData data;
 | 
						|
        data.variable = name;
 | 
						|
        data.iname = parent.iname;
 | 
						|
        //data.iname = data.variable;
 | 
						|
        data.exp = parent.exp;
 | 
						|
        data.setTypeUnneeded();
 | 
						|
        data.setValueUnneeded();
 | 
						|
        data.setChildCountUnneeded();
 | 
						|
        data.setChildrenUnneeded();
 | 
						|
        //qDebug() << "DATA" << data.toString();
 | 
						|
        QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
 | 
						|
        //iname += '.' + exp;
 | 
						|
        sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
 | 
						|
    } else if (item.findChild("numchild").data() == "0") {
 | 
						|
        // happens for structs without data, e.g. interfaces.
 | 
						|
        WatchData data;
 | 
						|
        data.iname = parent.iname + '.' + exp;
 | 
						|
        data.name = exp;
 | 
						|
        data.variable = name;
 | 
						|
        setWatchDataType(data, item.findChild("type"));
 | 
						|
        setWatchDataValue(data, item.findChild("value"));
 | 
						|
        setWatchDataAddress(data, item.findChild("addr"));
 | 
						|
        data.setChildCount(0);
 | 
						|
        insertData(data);
 | 
						|
    } else if (parent.iname.endsWith('.')) {
 | 
						|
        // Happens with anonymous unions
 | 
						|
        WatchData data;
 | 
						|
        data.iname = name;
 | 
						|
        QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
 | 
						|
        sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
 | 
						|
    } else if (exp == "staticMetaObject") {
 | 
						|
        //    && item.findChild("type").data() == "const QMetaObject")
 | 
						|
        // FIXME: Namespaces?
 | 
						|
        // { do nothing }    FIXME: make coinfigurable?
 | 
						|
        // special "clever" hack to avoid clutter in the GUI.
 | 
						|
        // I am not sure this is a good idea...
 | 
						|
    } else {
 | 
						|
        WatchData data;
 | 
						|
        data.iname = parent.iname + '.' + exp;
 | 
						|
        data.variable = name;
 | 
						|
        setWatchDataType(data, item.findChild("type"));
 | 
						|
        setWatchDataValue(data, item.findChild("value"));
 | 
						|
        setWatchDataAddress(data, item.findChild("addr"));
 | 
						|
        setWatchDataChildCount(data, item.findChild("numchild"));
 | 
						|
        if (!qq->watchHandler()->isExpandedIName(data.iname))
 | 
						|
            data.setChildrenUnneeded();
 | 
						|
 | 
						|
        data.name = exp;
 | 
						|
        if (isPointerType(parent.type) && data.type == exp) {
 | 
						|
            data.exp = "*(" + parent.exp + ")";
 | 
						|
            data.name = "*" + parent.name;
 | 
						|
        } else if (data.type == exp) {
 | 
						|
            // A type we derive from? gdb crashes when creating variables here
 | 
						|
            data.exp = parent.exp;
 | 
						|
        } else if (exp.startsWith("*")) {
 | 
						|
            // A pointer
 | 
						|
            data.exp = "*(" + parent.exp + ")";
 | 
						|
        } else if (startsWithDigit(exp)) {
 | 
						|
            // An array. No variables needed?
 | 
						|
            data.name = "[" + data.name + "]";
 | 
						|
            data.exp = parent.exp + "[" + exp + "]";
 | 
						|
        } else if (0 && parent.name.endsWith('.')) {
 | 
						|
            // Happens with anonymous unions
 | 
						|
            data.exp = parent.exp + exp;
 | 
						|
            //data.name = "<anonymous union>";
 | 
						|
        } else if (exp.isEmpty()) {
 | 
						|
            // Happens with anonymous unions
 | 
						|
            data.exp = parent.exp;
 | 
						|
            data.name = "<n/a>";
 | 
						|
            data.iname = parent.iname + ".@";
 | 
						|
            data.type = "<anonymous union>";
 | 
						|
        } else {
 | 
						|
            // A structure. Hope there's nothing else...
 | 
						|
            data.exp = parent.exp + '.' + exp;
 | 
						|
        }
 | 
						|
 | 
						|
        if (isCustomValueDumperAvailable(data.type)) {
 | 
						|
            // we do not trust gdb if we have a custom dumper
 | 
						|
            data.setValueNeeded();
 | 
						|
            data.setChildCountNeeded();
 | 
						|
        }
 | 
						|
 | 
						|
        //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
 | 
						|
        //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
 | 
						|
        insertData(data);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
 | 
						|
    const WatchData &data0)
 | 
						|
{
 | 
						|
    //WatchResultCounter dummy(this, WatchVarListChildren);
 | 
						|
    WatchData data = data0;
 | 
						|
    if (!data.isValid())
 | 
						|
        return;
 | 
						|
    if (record.resultClass == GdbResultDone) {
 | 
						|
        //qDebug() <<  "VAR_LIST_CHILDREN: PARENT " << data.toString();
 | 
						|
        GdbMi children = record.data.findChild("children");
 | 
						|
 | 
						|
        foreach (const GdbMi &child, children.children())
 | 
						|
            handleVarListChildrenHelper(child, data);
 | 
						|
 | 
						|
        if (!isAccessSpecifier(data.variable.split('.').takeLast())) {
 | 
						|
            data.setChildrenUnneeded();
 | 
						|
            insertData(data);
 | 
						|
        }
 | 
						|
    } else if (record.resultClass == GdbResultError) {
 | 
						|
        data.setError(record.data.findChild("msg").data());
 | 
						|
    } else {
 | 
						|
        data.setError("Unknown error: " + record.toString());
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void GdbEngine::handleToolTip(const GdbResultRecord &record,
 | 
						|
        const QString &what)
 | 
						|
{
 | 
						|
    //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString();
 | 
						|
    //    << "record: " << record.toString();
 | 
						|
    if (record.resultClass == GdbResultError) {
 | 
						|
        QString msg = record.data.findChild("msg").data();
 | 
						|
        if (what == "create") {
 | 
						|
            sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (what == "evaluate") {
 | 
						|
            if (msg.startsWith("Cannot look up value of a typedef")) {
 | 
						|
                m_toolTip.value = m_toolTip.exp + " is a typedef.";
 | 
						|
                //return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else if (record.resultClass == GdbResultDone) {
 | 
						|
        if (what == "create") {
 | 
						|
            setWatchDataType(m_toolTip, record.data.findChild("type"));
 | 
						|
            setWatchDataChildCount(m_toolTip, record.data.findChild("numchild"));
 | 
						|
            if (isCustomValueDumperAvailable(m_toolTip.type))
 | 
						|
                runCustomDumper(m_toolTip, false);
 | 
						|
            else
 | 
						|
                q->showStatusMessage(tr("Retrieving data for tooltip..."), 10000);
 | 
						|
                sendCommand("-data-evaluate-expression " + m_toolTip.exp,
 | 
						|
                    WatchToolTip, "evaluate");
 | 
						|
                //sendToolTipCommand("-var-evaluate-expression tooltip")
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (what == "evaluate") {
 | 
						|
            m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp
 | 
						|
                   + " = " + record.data.findChild("value").data();
 | 
						|
            //return;
 | 
						|
        }
 | 
						|
        if (what == "ptype") {
 | 
						|
            GdbMi mi = record.data.findChild("consolestreamoutput");
 | 
						|
            m_toolTip.value = extractTypeFromPTypeOutput(mi.data());
 | 
						|
            //return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    m_toolTip.iname = tooltipIName;
 | 
						|
    m_toolTip.setChildrenUnneeded();
 | 
						|
    m_toolTip.setChildCountUnneeded();
 | 
						|
    insertData(m_toolTip);
 | 
						|
    qDebug() << "DATA INSERTED";
 | 
						|
    QTimer::singleShot(0, this, SLOT(updateWatchModel2()));
 | 
						|
    qDebug() << "HANDLE TOOLTIP END";
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
void GdbEngine::handleChangedItem(QStandardItem *item)
 | 
						|
{
 | 
						|
    // HACK: Just store the item for the slot
 | 
						|
    //  handleChangedItem(QWidget *widget) below.
 | 
						|
    QModelIndex index = item->index().sibling(item->index().row(), 0);
 | 
						|
    //WatchData data = m_currentSet.takeData(iname);
 | 
						|
    //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
 | 
						|
    //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
 | 
						|
{
 | 
						|
    sendCommand("-var-delete assign");
 | 
						|
    sendCommand("-var-create assign * " + expression);
 | 
						|
    sendCommand("-var-assign assign " + value, WatchVarAssign);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void GdbEngine::tryLoadCustomDumpers()
 | 
						|
{
 | 
						|
    if (m_dataDumperState != DataDumperUninitialized)
 | 
						|
        return;
 | 
						|
 | 
						|
    PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
 | 
						|
    m_dataDumperState = DataDumperLoadTried;
 | 
						|
 | 
						|
#if defined(Q_OS_LINUX)
 | 
						|
    QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so";
 | 
						|
    if (QFileInfo(lib).isExecutable()) {
 | 
						|
        //sendCommand("p dlopen");
 | 
						|
        if (qq->useFastStart())
 | 
						|
            sendCommand("set stop-on-solib-events 0");
 | 
						|
        QString flag = QString::number(RTLD_NOW);
 | 
						|
        sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")");
 | 
						|
        // some older systems like CentOS 4.6 prefer this:
 | 
						|
        sendCommand("call (void)__dlopen(\"" + lib + "\", " + flag + ")");
 | 
						|
        sendCommand("sharedlibrary " + dotEscape(lib));
 | 
						|
        if (qq->useFastStart())
 | 
						|
            sendCommand("set stop-on-solib-events 1");
 | 
						|
    } else {
 | 
						|
        qDebug() << "DEBUG HELPER LIBRARY IS NOT USABLE: "
 | 
						|
            << lib << QFileInfo(lib).isExecutable();
 | 
						|
    }
 | 
						|
#endif
 | 
						|
#if defined(Q_OS_MAC)
 | 
						|
    QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib";
 | 
						|
    if (QFileInfo(lib).isExecutable()) {
 | 
						|
        //sendCommand("p dlopen"); // FIXME: remove me
 | 
						|
        if (qq->useFastStart())
 | 
						|
            sendCommand("set stop-on-solib-events 0");
 | 
						|
        QString flag = QString::number(RTLD_NOW);
 | 
						|
        sendCommand("call (void)dlopen(\"" + lib + "\", " + flag + ")");
 | 
						|
        sendCommand("sharedlibrary " + dotEscape(lib));
 | 
						|
        if (qq->useFastStart())
 | 
						|
            sendCommand("set stop-on-solib-events 1");
 | 
						|
    } else {
 | 
						|
        qDebug() << "DEBUG HELPER LIBRARY IS NOT USABLE: "
 | 
						|
            << lib << QFileInfo(lib).isExecutable();
 | 
						|
    }
 | 
						|
#endif
 | 
						|
#if defined(Q_OS_WIN)
 | 
						|
    QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll";
 | 
						|
    if (QFileInfo(lib).exists()) {
 | 
						|
        if (qq->useFastStart())
 | 
						|
            sendCommand("set stop-on-solib-events 0");
 | 
						|
        //sendCommand("handle SIGSEGV pass stop print");
 | 
						|
        //sendCommand("set unwindonsignal off");
 | 
						|
        sendCommand("call LoadLibraryA(\"" + lib + "\")");
 | 
						|
        sendCommand("sharedlibrary " + dotEscape(lib));
 | 
						|
        if (qq->useFastStart())
 | 
						|
            sendCommand("set stop-on-solib-events 1");
 | 
						|
    } else {
 | 
						|
        qDebug() << "DEBUG HELPER LIBRARY IS NOT USABLE: "
 | 
						|
            << lib << QFileInfo(lib).isExecutable();
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    // retreive list of dumpable classes
 | 
						|
    sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)",
 | 
						|
        GdbQueryDataDumper1);
 | 
						|
    sendCommand("p (char*)qDumpOutBuffer", GdbQueryDataDumper2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
IDebuggerEngine *createGdbEngine(DebuggerManager *parent)
 | 
						|
{
 | 
						|
    return new GdbEngine(parent);
 | 
						|
}
 | 
						|
 |