Skip to content
Snippets Groups Projects
breakhandler.cpp 36.6 KiB
Newer Older
/**************************************************************************
con's avatar
con committed
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
**
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
**
**************************************************************************/
hjk's avatar
hjk committed

con's avatar
con committed
#include "breakhandler.h"
#include "breakpointmarker.h"
con's avatar
con committed

#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerstringutils.h"
hjk's avatar
hjk committed
#include <utils/qtcassert.h>
hjk's avatar
hjk committed

#include <QtCore/QDir>
con's avatar
con committed
#include <QtCore/QFileInfo>
hjk's avatar
hjk committed
#include <QtCore/QTimerEvent>
con's avatar
con committed

con's avatar
con committed
//////////////////////////////////////////////////////////////////
//
// BreakHandler
//
//////////////////////////////////////////////////////////////////

namespace Debugger {
namespace Internal {

BreakHandler::BreakHandler()
con's avatar
con committed

hjk's avatar
hjk committed
BreakHandler::~BreakHandler()
hjk's avatar
hjk committed

QIcon BreakHandler::breakpointIcon()
{
    static QIcon icon(_(":/debugger/images/breakpoint_16.png"));
    return icon;
}

QIcon BreakHandler::disabledBreakpointIcon()
{
    static QIcon icon(_(":/debugger/images/breakpoint_disabled_16.png"));
    return icon;
}

QIcon BreakHandler::pendingBreakPointIcon()
{
    static QIcon icon(_(":/debugger/images/breakpoint_pending_16.png"));
    return icon;
}

QIcon BreakHandler::emptyIcon()
{
    static QIcon icon(_(":/debugger/images/breakpoint_pending_16.png"));
    //static QIcon icon(_(":/debugger/images/watchpoint.png"));
    //static QIcon icon(_(":/debugger/images/debugger_empty_14.png"));
    return icon;
}

con's avatar
con committed
int BreakHandler::columnCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : 8;
con's avatar
con committed
}

int BreakHandler::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : m_storage.size();
con's avatar
con committed
}

static inline bool fileNameMatch(const QString &f1, const QString &f2)
hjk's avatar
hjk committed
{
#ifdef Q_OS_WIN
    return f1.compare(f2, Qt::CaseInsensitive) == 0;
#else
    return f1 == f2;
#endif
hjk's avatar
hjk committed
}

static bool isSimilarTo(const BreakpointParameters &data, const BreakpointResponse &needle)
con's avatar
con committed
{
    // Clear hit.
    // Clear miss.
    if (needle.type != UnknownType && data.type != UnknownType
            && data.type != needle.type)
        return false;
con's avatar
con committed

    // Clear hit.
    if (data.address && data.address == needle.address)
        return true;

    // At least at a position we were looking for.
    // FIXME: breaks multiple breakpoints at the same location
    if (!data.fileName.isEmpty()
            && fileNameMatch(data.fileName, needle.fileName)
            && data.lineNumber == needle.lineNumber)
        return true;

    // At least at a position we were looking for.
    // FIXME: breaks multiple breakpoints at the same location
    if (!data.fileName.isEmpty()
            && fileNameMatch(data.fileName, needle.fileName)
            && data.lineNumber == needle.lineNumber)
        return true;

    return false;
con's avatar
con committed
}

BreakpointId BreakHandler::findSimilarBreakpoint(const BreakpointResponse &needle) const
con's avatar
con committed
{
    // Search a breakpoint we might refer to.
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it) {
        const BreakpointId id = it.key();
        const BreakpointParameters &data = it->data;
        const BreakpointResponse &response = it->response;
        qDebug() << "COMPARING " << data.toString() << " WITH " << needle.toString();
        if (response.number && response.number == needle.number)
            return id;

        if (isSimilarTo(data, needle))
            return id;
con's avatar
con committed
    }
    return BreakpointId(-1);
con's avatar
con committed
}

BreakpointId BreakHandler::findBreakpointByNumber(int bpNumber) const
con's avatar
con committed
{
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
        if (it->response.number == bpNumber)
            return it.key();
    return BreakpointId(-1);
con's avatar
con committed
}

BreakpointId BreakHandler::findBreakpointByFunction(const QString &functionName) const
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
        if (it->data.functionName == functionName)
            return it.key();
    return BreakpointId(-1);
}

BreakpointId BreakHandler::findBreakpointByAddress(quint64 address) const
{
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
        if (it->data.address == address)
            return it.key();
    return BreakpointId(-1);
}

BreakpointId BreakHandler::findBreakpointByFileAndLine(const QString &fileName,
    int lineNumber, bool useMarkerPosition)
{
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
        if (it->isLocatedAt(fileName, lineNumber, useMarkerPosition))
            return it.key();
    return BreakpointId(-1);
}

const BreakpointParameters &BreakHandler::breakpointData(BreakpointId id) const
    static BreakpointParameters dummy;
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return dummy);
}

BreakpointId BreakHandler::findWatchpointByAddress(quint64 address) const
{
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
        if (it->data.isWatchpoint() && it->data.address == address)
            return it.key();
    return BreakpointId(-1);
}

void BreakHandler::setWatchpointByAddress(quint64 address)
{
    const int id = findWatchpointByAddress(address);
    if (id == -1) {
        BreakpointParameters data(Watchpoint);
        data.address = address;
        appendBreakpoint(data);
        scheduleSynchronization();
    } else {
        qDebug() << "WATCHPOINT EXISTS";
     //   removeBreakpoint(index);
bool BreakHandler::hasWatchpointAt(quint64 address) const
    return findWatchpointByAddress(address) != BreakpointId(-1);
con's avatar
con committed
void BreakHandler::saveBreakpoints()
{
    //qDebug() << "SAVING BREAKPOINTS...";
    QTC_ASSERT(debuggerCore(), return);
con's avatar
con committed
    QList<QVariant> list;
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it) {
        const BreakpointParameters &data = it->data;
con's avatar
con committed
        QMap<QString, QVariant> map;
        // Do not persist Watchpoints.
        if (data.isWatchpoint())
            continue;
        if (data.type != BreakpointByFileAndLine)
            map.insert(_("type"), data.type);
        if (!data.fileName.isEmpty())
            map.insert(_("filename"), data.fileName);
        if (data.lineNumber)
            map.insert(_("linenumber"), data.lineNumber);
        if (!data.functionName.isEmpty())
            map.insert(_("funcname"), data.functionName);
        if (data.address)
            map.insert(_("address"), data.address);
        if (!data.condition.isEmpty())
            map.insert(_("condition"), data.condition);
        if (data.ignoreCount)
            map.insert(_("ignorecount"), data.ignoreCount);
        if (!data.threadSpec.isEmpty())
            map.insert(_("threadspec"), data.threadSpec);
        if (!data.enabled)
            map.insert(_("disabled"), _("1"));
        if (data.useFullPath)
            map.insert(_("usefullpath"), _("1"));
con's avatar
con committed
        list.append(map);
    }
    debuggerCore()->setSessionValue("Breakpoints", list);
    //qDebug() << "SAVED BREAKPOINTS" << this << list.size();
con's avatar
con committed
}

void BreakHandler::loadBreakpoints()
{
    QTC_ASSERT(debuggerCore(), return);
    //qDebug() << "LOADING BREAKPOINTS...";
    QVariant value = debuggerCore()->sessionValue("Breakpoints");
con's avatar
con committed
    QList<QVariant> list = value.toList();
    //clear();
con's avatar
con committed
    foreach (const QVariant &var, list) {
        const QMap<QString, QVariant> map = var.toMap();
        BreakpointParameters data(BreakpointByFileAndLine);
        QVariant v = map.value(_("filename"));
            data.fileName = v.toString();
        v = map.value(_("linenumber"));
            data.lineNumber = v.toString().toInt();
        v = map.value(_("condition"));
            data.condition = v.toString().toLatin1();
        v = map.value(_("address"));
        if (v.isValid())
            data.address = v.toString().toULongLong();
        v = map.value(_("ignorecount"));
            data.ignoreCount = v.toString().toInt();
        v = map.value(_("threadspec"));
        if (v.isValid())
            data.threadSpec = v.toString().toLatin1();
        v = map.value(_("funcname"));
            data.functionName = v.toString();
        v = map.value(_("disabled"));
            data.enabled = !v.toInt();
        v = map.value(_("usefullpath"));
            data.useFullPath = bool(v.toInt());
        v = map.value(_("type"));
        if (v.isValid() && v.toInt() != UnknownType)
            data.type = BreakpointType(v.toInt());
        appendBreakpoint(data);
con's avatar
con committed
    }
    //qDebug() << "LOADED BREAKPOINTS" << this << list.size();
con's avatar
con committed
}

void BreakHandler::updateMarkers()
{
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
        updateMarker(it.key());
con's avatar
con committed
}

void BreakHandler::updateMarker(BreakpointId id)
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    QString markerFileName = it->markerFileName();
    int markerLineNumber = it->markerLineNumber();
    if (it->marker && (markerFileName != it->marker->fileName()
                || markerLineNumber != it->marker->lineNumber()))
    if (!it->marker && !markerFileName.isEmpty() && markerLineNumber > 0)
        it->marker = new BreakpointMarker(id, markerFileName, markerLineNumber);
con's avatar
con committed
QVariant BreakHandler::headerData(int section,
    Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
        static QString headers[] = {
            tr("Number"),  tr("Function"), tr("File"), tr("Line"),
            tr("Condition"), tr("Ignore"), tr("Threads"), tr("Address")
con's avatar
con committed
        };
        return headers[section];
    }
    return QVariant();
}

BreakpointId BreakHandler::findBreakpointByIndex(const QModelIndex &index) const
{
    int r = index.row();
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for (int i = 0; it != et; ++it, ++i)
        if (i == r)
            return it.key();
    return BreakpointId(-1);
}

BreakpointIds BreakHandler::findBreakpointsByIndex(const QList<QModelIndex> &list) const
{
    BreakpointIds ids;
    foreach (const QModelIndex &index, list)
        ids.append(findBreakpointByIndex(index));
    return ids;
}

Qt::ItemFlags BreakHandler::flags(const QModelIndex &index) const
{
//    switch (index.column()) {
//        //case 0:
//        //    return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
//        default:
            return QAbstractTableModel::flags(index);
//    }
}

con's avatar
con committed
QVariant BreakHandler::data(const QModelIndex &mi, int role) const
{
    static const QString empty = QString(QLatin1Char('-'));

    if (!mi.isValid())
        return QVariant();

    BreakpointId id = findBreakpointByIndex(mi);
    //qDebug() << "DATA: " << id << role << mi.column();
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return QVariant());
    const BreakpointParameters &data = it->data;
    const BreakpointResponse &response = it->response;
con's avatar
con committed
    switch (mi.column()) {
        case 0:
            if (role == Qt::DisplayRole) {
                return QString::number(id);
                //return QString("%1 - %2").arg(id).arg(response.number);
con's avatar
con committed
            }
            if (role == Qt::DecorationRole)
                return it->icon();
con's avatar
con committed
            break;
        case 1:
            if (role == Qt::DisplayRole) {
                if (!response.functionName.isEmpty())
                    return response.functionName;
                if (!data.functionName.isEmpty())
                    return data.functionName;
                return empty;
con's avatar
con committed
            }
            break;
        case 2:
            if (role == Qt::DisplayRole) {
                QString str;
                if (!response.fileName.isEmpty())
                    str = response.fileName;
                if (str.isEmpty() && !data.fileName.isEmpty())
                    str = data.fileName;
                if (str.isEmpty()) {
                    QString s = QFileInfo(str).fileName();
                    if (!s.isEmpty())
                        str = s;
                }
hjk's avatar
hjk committed
                // FIXME: better?
                //if (data.multiple && str.isEmpty() && !response.fileName.isEmpty())
                //    str = response.fileName;
                if (!str.isEmpty())
                    return str;
                return empty;
con's avatar
con committed
            }
            break;
        case 3:
            if (role == Qt::DisplayRole) {
                if (response.lineNumber > 0)
                    return response.lineNumber;
                if (data.lineNumber > 0)
                    return data.lineNumber;
                return empty;
con's avatar
con committed
            }
            if (role == Qt::UserRole + 1)
                return data.lineNumber;
con's avatar
con committed
            break;
        case 4:
            if (role == Qt::DisplayRole)
                return it->isPending() ? data.condition : response.condition;
con's avatar
con committed
            if (role == Qt::ToolTipRole)
                return tr("Breakpoint will only be hit if this condition is met.");
            if (role == Qt::UserRole + 1)
                return data.condition;
con's avatar
con committed
            break;
        case 5:
            if (role == Qt::DisplayRole) {
                const int ignoreCount =
                    it->isPending() ? data.ignoreCount : response.ignoreCount;
                return ignoreCount ? QVariant(ignoreCount) : QVariant(QString());
            }
con's avatar
con committed
            if (role == Qt::ToolTipRole)
                return tr("Breakpoint will only be hit after being ignored so many times.");
            if (role == Qt::UserRole + 1)
                return data.ignoreCount;
            if (role == Qt::DisplayRole) {
                    return !data.threadSpec.isEmpty() ? data.threadSpec : tr("(all)");
                    return !response.threadSpec.isEmpty() ? response.threadSpec : tr("(all)");
            }
            if (role == Qt::ToolTipRole)
                return tr("Breakpoint will only be hit in the specified thread(s).");
            if (role == Qt::UserRole + 1)
                return data.threadSpec;
            if (role == Qt::DisplayRole) {
                const quint64 address =
                    data.isWatchpoint() ? data.address : response.address;
                if (address)
                    displayValue += QString::fromAscii("0x%1").arg(address, 0, 16);
hjk's avatar
hjk committed
                if (!response.extra.isEmpty()) {
                    if (!displayValue.isEmpty())
                        displayValue += QLatin1Char(' ');
hjk's avatar
hjk committed
                    displayValue += QString::fromAscii(response.extra);
con's avatar
con committed
            break;
    }
    if (role == Qt::ToolTipRole)
        return debuggerCore()->boolSetting(UseToolTipsInBreakpointsView)
                ? QVariant(it->toToolTip()) : QVariant();
con's avatar
con committed
    return QVariant();
}

#define GETTER(type, getter) \
type BreakHandler::getter(BreakpointId id) const \
{ \
    ConstIterator it = m_storage.find(id); \
    QTC_ASSERT(it != m_storage.end(), \
        qDebug() << "ID" << id << "NOT KNOWN"; \
        return type()); \
    return it->data.getter; \
#define SETTER(type, getter, setter) \
void BreakHandler::setter(BreakpointId id, const type &value) \
{ \
    Iterator it = m_storage.find(id); \
    QTC_ASSERT(it != m_storage.end(), \
        qDebug() << "ID" << id << "NOT KNOWN"; return); \
    if (it->data.getter == value) \
    it->data.getter = value; \
    it->state = BreakpointChangeRequested; \
    scheduleSynchronization(); \
#define PROPERTY(type, getter, setter) \
    GETTER(type, getter) \
    SETTER(type, getter, setter)
con's avatar
con committed

PROPERTY(bool, useFullPath, setUseFullPath)
PROPERTY(QString, fileName, setFileName)
PROPERTY(QString, functionName, setFunctionName)
PROPERTY(BreakpointType, type, setType)
PROPERTY(QByteArray, threadSpec, setThreadSpec)
PROPERTY(QByteArray, condition, setCondition)
PROPERTY(int, lineNumber, setLineNumber)
PROPERTY(quint64, address, setAddress)
PROPERTY(int, ignoreCount, setIgnoreCount)
bool BreakHandler::isEnabled(BreakpointId id) const
{
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return false);
    return it->data.enabled;
}

void BreakHandler::setEnabled(BreakpointId id, bool on)
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    //qDebug() << "SET ENABLED: " << id << it->data.isEnabled() << on;
    if (it->data.enabled == on)
        return;
    it->data.enabled = on;
    it->destroyMarker();
    it->state = BreakpointChangeRequested;
    updateMarker(id);
    scheduleSynchronization();
void BreakHandler::setMarkerFileAndLine(BreakpointId id,
    const QString &fileName, int lineNumber)
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    if (it->response.fileName == fileName && it->response.lineNumber == lineNumber)
        return;
    it->response.fileName = fileName;
    it->response.lineNumber = lineNumber;
BreakpointState BreakHandler::state(BreakpointId id) const
{
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return BreakpointDead);
    return it->state;
}

DebuggerEngine *BreakHandler::engine(BreakpointId id) const
{
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return 0);
    return it->engine;
}

void BreakHandler::setEngine(BreakpointId id, DebuggerEngine *value)
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    QTC_ASSERT(it->state == BreakpointNew, /**/);
    QTC_ASSERT(!it->engine, return);
    it->engine = value;
    it->state = BreakpointInsertRequested;
    it->response = BreakpointResponse();
    it->response.fileName = it->data.fileName;
    updateMarker(id);
    scheduleSynchronization();
}

static bool isAllowedTransition(BreakpointState from, BreakpointState to)
{
    switch (from) {
    case BreakpointNew:
        return to == BreakpointInsertRequested;
    case BreakpointInsertRequested:
        return to == BreakpointInsertProceeding;
    case BreakpointInsertProceeding:
        return to == BreakpointInserted
            || to == BreakpointDead
            || to == BreakpointChangeRequested;
    case BreakpointChangeRequested:
        return to == BreakpointChangeProceeding;
    case BreakpointChangeProceeding:
        return to == BreakpointInserted
            || to == BreakpointDead;
    case BreakpointInserted:
        return to == BreakpointChangeRequested
            || to == BreakpointRemoveRequested;
    case BreakpointRemoveRequested:
        return to == BreakpointRemoveProceeding;
    case BreakpointRemoveProceeding:
        return to == BreakpointDead;
    case BreakpointDead:
        return false;
    }
    qDebug() << "UNKNOWN BREAKPOINT STATE:" << from;
    return false;
}

void BreakHandler::setState(BreakpointId id, BreakpointState state)
{
    Iterator it = m_storage.find(id);
    //qDebug() << "BREAKPOINT STATE TRANSITION" << id << it->state << state;
    QTC_ASSERT(it != m_storage.end(), return);
    QTC_ASSERT(isAllowedTransition(it->state, state),
        qDebug() << "UNEXPECTED BREAKPOINT STATE TRANSITION"
            << it->state << state);

    if (it->state == state) {
        qDebug() << "STATE UNCHANGED: " << id << state;
        return;
    }
void BreakHandler::notifyBreakpointInsertProceeding(BreakpointId id)
    QTC_ASSERT(state(id) == BreakpointInsertRequested, /**/);
    setState(id, BreakpointInsertProceeding);
void BreakHandler::notifyBreakpointInsertOk(BreakpointId id)
{
    QTC_ASSERT(state(id) == BreakpointInsertProceeding, /**/);
    setState(id, BreakpointInserted);
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    //if (it0->needsChange(it->data, it->response)) {
    //    setState(id, BreakpointChangeRequested);
    //    scheduleSynchronization();
    //}
}

void BreakHandler::notifyBreakpointInsertFailed(BreakpointId id)
{
    QTC_ASSERT(state(id) == BreakpointInsertProceeding, /**/);
    setState(id, BreakpointDead);
}

void BreakHandler::notifyBreakpointRemoveProceeding(BreakpointId id)
{
    QTC_ASSERT(state(id) == BreakpointRemoveRequested, /**/);
    setState(id, BreakpointRemoveProceeding);
}

void BreakHandler::notifyBreakpointRemoveOk(BreakpointId id)
{
    QTC_ASSERT(state(id) == BreakpointRemoveProceeding, /**/);
    setState(id, BreakpointDead);
    cleanupBreakpoint(id);
}

void BreakHandler::notifyBreakpointRemoveFailed(BreakpointId id)
{
    QTC_ASSERT(state(id) == BreakpointRemoveProceeding, /**/);
    setState(id, BreakpointDead);
    cleanupBreakpoint(id);
}

void BreakHandler::notifyBreakpointChangeOk(BreakpointId id)
{
    QTC_ASSERT(state(id) == BreakpointChangeProceeding, /**/);
    setState(id, BreakpointInserted);
}

void BreakHandler::notifyBreakpointChangeFailed(BreakpointId id)
    QTC_ASSERT(state(id) == BreakpointChangeProceeding, /**/);
    setState(id, BreakpointDead);
}

void BreakHandler::notifyBreakpointReleased(BreakpointId id)
{
    //QTC_ASSERT(state(id) == BreakpointChangeProceeding, /**/);
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    it->state = BreakpointNew;
    it->engine = 0;
    it->response = BreakpointResponse();
    delete it->marker;
    it->marker = 0;
    updateMarker(id);
void BreakHandler::notifyBreakpointAdjusted(BreakpointId id,
        const BreakpointParameters &data)
{
    QTC_ASSERT(state(id) == BreakpointInserted, /**/);
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    it->data = data;
    if (it->needsChange())
        setState(id, BreakpointChangeRequested);
}


void BreakHandler::ackCondition(BreakpointId id)
con's avatar
con committed
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    it->response.condition = it->data.condition;
    updateMarker(id);
con's avatar
con committed
}

void BreakHandler::ackIgnoreCount(BreakpointId id)
con's avatar
con committed
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    it->response.ignoreCount = it->data.ignoreCount;
    updateMarker(id);
con's avatar
con committed
}

void BreakHandler::ackEnabled(BreakpointId id)
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    it->response.enabled = it->data.enabled;
void BreakHandler::removeBreakpoint(BreakpointId id)
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    if (it->state == BreakpointInserted) {
        setState(id, BreakpointRemoveRequested);
    } else if (it->state == BreakpointNew) {
        it->state = BreakpointDead;
        cleanupBreakpoint(id);
        qDebug() << "CANNOT REMOVE IN STATE " << it->state;
        it->state = BreakpointRemoveRequested;
con's avatar
con committed

void BreakHandler::appendBreakpoint(const BreakpointParameters &data)
con's avatar
con committed
{
    QTC_ASSERT(data.type != UnknownType, return);
    // Ok to be not thread-safe. The order does not matter and only the gui
    // produces authoritative ids.
    static quint64 currentId = 0;
con's avatar
con committed

    BreakpointId id(++currentId);
    BreakpointItem item;
    item.response.fileName = data.fileName;
    item.response.lineNumber = data.lineNumber;
    m_storage.insert(id, item);
    scheduleSynchronization();
void BreakHandler::toggleBreakpoint(const QString &fileName, int lineNumber,
                                    quint64 address /* = 0 */)
con's avatar
con committed
{
    BreakpointId id(-1);
        id = findBreakpointByAddress(address);
        id = findBreakpointByFileAndLine(fileName, lineNumber, true);
        if (id == BreakpointId(-1))
            id = findBreakpointByFileAndLine(fileName, lineNumber, false);
    if (id != BreakpointId(-1)) {
        removeBreakpoint(id);
        BreakpointParameters data;
            data.type = BreakpointByAddress;
            data.address = address;
            data.type = BreakpointByFileAndLine;
            data.fileName = fileName;
            data.lineNumber = lineNumber;
    debuggerCore()->synchronizeBreakpoints();
con's avatar
con committed
void BreakHandler::saveSessionData()
{
    saveBreakpoints();
}

void BreakHandler::loadSessionData()
{
con's avatar
con committed
    loadBreakpoints();
}

void BreakHandler::breakByFunction(const QString &functionName)
{
hjk's avatar
hjk committed
    // One breakpoint per function is enough for now. This does not handle
    // combinations of multiple conditions and ignore counts, though.
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it) {
        const BreakpointParameters &data = it->data;
        if (data.functionName == functionName
                && data.condition.isEmpty()
                && data.ignoreCount == 0)
con's avatar
con committed
            return;
    }
    BreakpointParameters data(BreakpointByFunction);
    data.functionName = functionName;
    appendBreakpoint(data);
}

QIcon BreakHandler::icon(BreakpointId id) const
{
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return pendingBreakPointIcon());
}

void BreakHandler::scheduleSynchronization()
{
    if (m_syncTimerId == -1)
        m_syncTimerId = startTimer(10);
}

void BreakHandler::timerEvent(QTimerEvent *event)
{
    QTC_ASSERT(event->timerId() == m_syncTimerId, return);
    killTimer(m_syncTimerId);
    m_syncTimerId = -1;
    //qDebug() << "BREAKPOINT SYNCRONIZATION STARTED";
    debuggerCore()->synchronizeBreakpoints();
con's avatar
con committed
    updateMarkers();
    emit layoutChanged();
    saveBreakpoints();  // FIXME: remove?
}

void BreakHandler::gotoLocation(BreakpointId id) const
{
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    debuggerCore()->gotoLocation(
        it->data.fileName, it->data.lineNumber, false);
}

void BreakHandler::updateLineNumberFromMarker(BreakpointId id, int lineNumber)
{
    Iterator it = m_storage.find(id);
    it->response.pending = false;
    QTC_ASSERT(it != m_storage.end(), return);
    //if (data.markerLineNumber == lineNumber)
    //    return;
    if (it->response.lineNumber != lineNumber) {
        it->response.lineNumber = lineNumber;
        // FIXME: Should we tell gdb about the change?
        // Ignore it for now, as we would require re-compilation
        // and debugger re-start anyway.
        //if (0 && data.bpLineNumber) {
        //    if (!data.bpNumber.trimmed().isEmpty()) {
        //        data.pending = true;
        //    }
        //}
    }
    // Ignore updates to the "real" line number while the debugger is
    // running, as this can be triggered by moving the breakpoint to
    // the next line that generated code.
    // FIXME: Do we need yet another data member?
    if (it->response.number == 0) {
        it->data.lineNumber = lineNumber;
}

BreakpointIds BreakHandler::allBreakpointIds() const
{
    BreakpointIds ids;
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
        ids.append(it.key());
    return ids;
}

BreakpointIds BreakHandler::unclaimedBreakpointIds() const
{
    return engineBreakpointIds(0);
}

BreakpointIds BreakHandler::engineBreakpointIds(DebuggerEngine *engine) const
{
    BreakpointIds ids;
    ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
    for ( ; it != et; ++it)
            ids.append(it.key());
    return ids;
}

void BreakHandler::cleanupBreakpoint(BreakpointId id)
{
    QTC_ASSERT(state(id) == BreakpointDead, /**/);
    BreakpointItem item = m_storage.take(id);
const BreakpointResponse &BreakHandler::response(BreakpointId id) const
    static BreakpointResponse dummy;
    ConstIterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return dummy);
}

void BreakHandler::setResponse(BreakpointId id, const BreakpointResponse &data)
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    //qDebug() << "SET RESPONSE: " << id << it->state << it->needsChange();
    if (it->state == BreakpointChangeProceeding
        || it->state == BreakpointInsertProceeding) {
        if (it->needsChange())
            setState(id, BreakpointChangeRequested);
        else
            setState(id, BreakpointInserted);
    }
    it->destroyMarker();
    updateMarker(id);
void BreakHandler::setBreakpointData(BreakpointId id, const BreakpointParameters &data)
{
    Iterator it = m_storage.find(id);
    QTC_ASSERT(it != m_storage.end(), return);
    if (data == it->data)
        return;
    updateMarker(id);
    layoutChanged();
}

//////////////////////////////////////////////////////////////////
//
// Storage
//
//////////////////////////////////////////////////////////////////

BreakHandler::BreakpointItem::BreakpointItem()
  : state(BreakpointNew), engine(0), marker(0)
void BreakHandler::BreakpointItem::destroyMarker()
    BreakpointMarker *m = marker;
QString BreakHandler::BreakpointItem::markerFileName() const
{
    // Some heuristics to find a "good" file name.
    if (!data.fileName.isEmpty()) {
        QFileInfo fi(data.fileName);
        if (fi.exists())
            return fi.absoluteFilePath();
    }
    if (!response.fileName.isEmpty()) {
        QFileInfo fi(response.fileName);
        if (fi.exists())
            return fi.absoluteFilePath();
    }
    if (response.fileName.endsWith(data.fileName))
        return response.fileName;
    if (data.fileName.endsWith(response.fileName))
        return data.fileName;
    return response.fileName.size() > data.fileName.size()
        ? response.fileName : data.fileName;