Commit d647a600 authored by hjk's avatar hjk Committed by hjk

debugger: display "sub-breakpoints"

<MULTIPLE> happens in constructors, inline functions, and
at other places like 'foreach' lines.

Change-Id: Ifb89b659d279f257ba8295b80a35d605820ec54b
Reviewed-on: http://codereview.qt.nokia.com/498Reviewed-by: default avatarQt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: default avatarhjk <qthjk@ovi.com>
parent cc43a597
......@@ -42,6 +42,10 @@
#include <utils/qtcassert.h>
#if USE_BREAK_MODEL_TEST
#include "modeltest.h"
#endif
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTimerEvent>
......@@ -126,7 +130,11 @@ static QString typeToString(BreakpointType type)
BreakHandler::BreakHandler()
: m_syncTimerId(-1)
{}
{
#if USE_BREAK_MODEL_TEST
new ModelTest(this, 0);
#endif
}
BreakHandler::~BreakHandler()
{}
......@@ -169,16 +177,6 @@ QIcon BreakHandler::emptyIcon()
return icon;
}
int BreakHandler::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : 8;
}
int BreakHandler::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_storage.size();
}
static inline bool fileNameMatch(const QString &f1, const QString &f2)
{
#ifdef Q_OS_WIN
......@@ -431,12 +429,9 @@ QVariant BreakHandler::headerData(int section,
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();
//qDebug() << "FIND: " << index <<
// BreakpointId::fromInternalId(index.internalId());
return BreakpointId::fromInternalId(index.internalId());
}
BreakpointIds BreakHandler::findBreakpointsByIndex(const QList<QModelIndex> &list) const
......@@ -469,15 +464,72 @@ int BreakHandler::threadSpecFromDisplay(const QString &str)
return ok ? result : -1;
}
QModelIndex BreakHandler::index(int row, int col, const QModelIndex &parent) const
QModelIndex BreakHandler::createIndex(int row, int column, quint32 id) const
{
Q_UNUSED(parent);
return createIndex(row, col, 0);
return QAbstractItemModel::createIndex(row, column, id);
}
QModelIndex BreakHandler::parent(const QModelIndex &parent) const
QModelIndex BreakHandler::createIndex(int row, int column, void *ptr) const
{
Q_UNUSED(parent);
QTC_ASSERT(false, /**/); // This function is not used.
return QAbstractItemModel::createIndex(row, column, ptr);
}
int BreakHandler::columnCount(const QModelIndex &idx) const
{
if (idx.column() > 0)
return 0;
const BreakpointId id = findBreakpointByIndex(idx);
return id.isMinor() ? 0 : 8;
}
int BreakHandler::rowCount(const QModelIndex &idx) const
{
if (idx.column() > 0)
return 0;
if (!idx.isValid())
return m_storage.size();
const BreakpointId id = findBreakpointByIndex(idx);
if (id.isMajor())
return m_storage.value(id).subItems.size();
return 0;
}
QModelIndex BreakHandler::index(int row, int col, const QModelIndex &parent) const
{
if (row < 0 || col < 0)
return QModelIndex();
if (parent.column() > 0)
return QModelIndex();
BreakpointId id = findBreakpointByIndex(parent);
if (id.isMajor()) {
ConstIterator it = m_storage.find(id);
if (row >= it->subItems.size())
return QModelIndex();
BreakpointId sub = id.child(row);
return createIndex(row, col, sub.toInternalId());
}
if (id.isMinor())
return QModelIndex();
QTC_ASSERT(!id.isValid(), return QModelIndex());
if (row >= m_storage.size())
return QModelIndex();
id = at(row);
return createIndex(row, col, id.toInternalId());
}
QModelIndex BreakHandler::parent(const QModelIndex &idx) const
{
if (!idx.isValid())
return QModelIndex();
BreakpointId id = findBreakpointByIndex(idx);
if (id.isMajor())
return QModelIndex();
if (id.isMinor()) {
BreakpointId pid = id.parent();
int row = indexOf(pid);
return createIndex(row, 0, pid.toInternalId());
}
return QModelIndex();
}
......@@ -489,9 +541,13 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
return QVariant();
BreakpointId id = findBreakpointByIndex(mi);
//qDebug() << "DATA: " << id << role << mi.column();
ConstIterator it = m_storage.find(id);
BREAK_ASSERT(it != m_storage.end(), return QVariant());
BreakpointId pid = id;
if (id.isMinor())
pid = id.parent();
ConstIterator it = m_storage.find(pid);
QTC_ASSERT(it != m_storage.end(), return QVariant());
const BreakpointParameters &data = it->data;
const BreakpointResponse &response = it->response;
......@@ -511,10 +567,24 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
break;
};
if (id.isMinor()) {
QTC_ASSERT(id.minorPart() <= it->subItems.size(), return QVariant());
const BreakpointResponse &res = it->subItems.at(id.minorPart() - 1);
switch (mi.column()) {
case 0:
if (role == Qt::DisplayRole)
return id.toString();
case 1:
if (role == Qt::DisplayRole)
return res.functionName;
}
return QVariant();
}
switch (mi.column()) {
case 0:
if (role == Qt::DisplayRole) {
return QString::number(id);
return id.toString();
//return QString("%1 - %2").arg(id).arg(response.number);
}
if (role == Qt::DecorationRole)
......@@ -894,6 +964,7 @@ void BreakHandler::notifyBreakpointReleased(BreakpointId id)
it->state = BreakpointNew;
it->engine = 0;
it->response = BreakpointResponse();
it->subItems.clear();
delete it->marker;
it->marker = 0;
if (it->data.type == WatchpointAtAddress
......@@ -937,8 +1008,8 @@ void BreakHandler::removeBreakpoint(BreakpointId id)
cleanupBreakpoint(id);
break;
default:
qWarning("Warning: Cannot remove breakpoint %llu in state '%s'.",
id, qPrintable(stateToString(it->state)));
qWarning("Warning: Cannot remove breakpoint %s in state '%s'.",
qPrintable(id.toString()), qPrintable(stateToString(it->state)));
it->state = BreakpointRemoveRequested;
break;
}
......@@ -963,9 +1034,42 @@ void BreakHandler::appendBreakpoint(const BreakpointParameters &data)
m_storage.insert(id, item);
endInsertRows();
layoutChanged();
updateMarker(id);
scheduleSynchronization();
}
BreakpointId BreakHandler::at(int n) const
{
if (n < 0 || n >= m_storage.size())
return BreakpointId();
ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
for ( ; --n >= 0; ++it)
;
return it.key();
}
int BreakHandler::indexOf(BreakpointId id) const
{
int row = 0;
ConstIterator it = m_storage.constBegin(), et = m_storage.constEnd();
for ( ; it != et; ++it, ++row)
if (it.key() == id)
return row;
return -1;
}
void BreakHandler::appendSubBreakpoint(BreakpointId id, const BreakpointResponse &data)
{
Iterator it = m_storage.find(id);
QTC_ASSERT(it != m_storage.end(), return);
int row = indexOf(id);
QTC_ASSERT(row != -1, return);
QModelIndex idx = createIndex(row, 0, id.toInternalId());
beginInsertRows(idx, it->subItems.size(), it->subItems.size());
it->subItems.append(data);
endInsertRows();
}
void BreakHandler::saveSessionData()
......
......@@ -70,6 +70,7 @@ public:
// The only way to add a new breakpoint.
void appendBreakpoint(const BreakpointParameters &data);
void appendSubBreakpoint(BreakpointId id, const BreakpointResponse &data);
BreakpointIds allBreakpointIds() const;
BreakpointIds engineBreakpointIds(DebuggerEngine *engine) const;
......@@ -169,7 +170,11 @@ private:
Qt::ItemFlags flags(const QModelIndex &index) const;
QModelIndex index(int row, int col, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &parent) const;
QModelIndex createIndex(int row, int column, quint32 id) const;
QModelIndex createIndex(int row, int column, void *ptr) const;
int indexOf(BreakpointId id) const;
BreakpointId at(int index) const;
bool isEngineRunning(BreakpointId id) const;
void setState(BreakpointId id, BreakpointState state);
void loadBreakpoints();
......@@ -195,6 +200,7 @@ private:
DebuggerEngine *engine; // Engine currently handling the breakpoint.
BreakpointResponse response;
BreakpointMarker *marker;
QList<BreakpointResponse> subItems;
};
typedef QHash<BreakpointId, BreakpointItem> BreakpointStorage;
typedef BreakpointStorage::ConstIterator ConstIterator;
......
......@@ -32,12 +32,47 @@
#include "breakpoint.h"
#include "utils/qtcassert.h"
#include <QtCore/QByteArray>
#include <QtCore/QDebug>
namespace Debugger {
namespace Internal {
//////////////////////////////////////////////////////////////////
//
// BreakpointId
//
//////////////////////////////////////////////////////////////////
QDebug operator<<(QDebug d, const BreakpointId &id)
{
d << qPrintable(id.toString());
return d;
}
QString BreakpointId::toString() const
{
if (!isValid())
return "<invalid bkpt>";
if (isMinor())
return QString("%1.%2").arg(m_majorPart).arg(m_minorPart);
return QString::number(m_majorPart);
}
BreakpointId BreakpointId::parent() const
{
QTC_ASSERT(isMinor(), return BreakpointId());
return BreakpointId(m_majorPart, 0);
}
BreakpointId BreakpointId::child(int row) const
{
QTC_ASSERT(isMajor(), return BreakpointId());
return BreakpointId(m_majorPart, row + 1);
}
//////////////////////////////////////////////////////////////////
//
// BreakpointParameters
......@@ -163,24 +198,34 @@ QString BreakpointParameters::toString() const
*/
BreakpointResponse::BreakpointResponse()
: number(0), pending(true), multiple(false), correctedLineNumber(0)
{}
{
number = 0;
subNumber = 0;
pending = true;
multiple = false;
correctedLineNumber = 0;
}
QString BreakpointResponse::toString() const
{
QString result = BreakpointParameters::toString();
QTextStream ts(&result);
ts << " Number: " << number;
if (subNumber)
ts << "." << subNumber;
if (pending)
ts << " [pending]";
if (!fullName.isEmpty())
ts << " FullName: " << fullName;
if (!functionName.isEmpty())
ts << " Function: " << functionName;
if (multiple)
ts << " Multiple: " << multiple;
if (!extra.isEmpty())
ts << " Extra: " << extra;
if (correctedLineNumber)
ts << " CorrectedLineNumber: " << correctedLineNumber;
ts << ' ';
return result + BreakpointParameters::toString();
}
......@@ -188,6 +233,7 @@ void BreakpointResponse::fromParameters(const BreakpointParameters &p)
{
BreakpointParameters::operator=(p);
number = 0;
subNumber = 0;
fullName.clear();
multiple = false;
extra.clear();
......
......@@ -33,6 +33,7 @@
#ifndef DEBUGGER_BREAKPOINT_H
#define DEBUGGER_BREAKPOINT_H
#include <QtCore/QDebug>
#include <QtCore/QList>
#include <QtCore/QMetaType>
#include <QtCore/QString>
......@@ -40,7 +41,36 @@
namespace Debugger {
namespace Internal {
typedef quint64 BreakpointId;
class BreakpointId
{
public:
BreakpointId() { m_majorPart = m_minorPart = 0; }
explicit BreakpointId(quint16 ma) { m_majorPart = ma; m_minorPart = 0; }
BreakpointId(quint16 ma, quint16 mi) { m_majorPart = ma; m_minorPart = mi; }
bool isValid() const { return m_majorPart != 0; }
bool isMajor() const { return m_majorPart != 0 && m_minorPart == 0; }
bool isMinor() const { return m_majorPart != 0 && m_minorPart != 0; }
bool operator!() const { return !isValid(); }
operator const void*() const { return isValid() ? this : 0; }
quint32 toInternalId() const { return m_majorPart | (m_minorPart << 16); }
QString toString() const;
bool operator==(const BreakpointId &id) const
{ return m_majorPart == id.m_majorPart && m_minorPart == id.m_minorPart; }
quint16 majorPart() const { return m_majorPart; }
quint16 minorPart() const { return m_minorPart; }
BreakpointId parent() const;
BreakpointId child(int row) const;
static BreakpointId fromInternalId(quint32 id)
{ return BreakpointId(id & 0xff, id >> 16); }
private:
quint16 m_majorPart;
quint16 m_minorPart;
};
QDebug operator<<(QDebug d, const BreakpointId &id);
//////////////////////////////////////////////////////////////////
//
......@@ -165,6 +195,7 @@ public:
void fromParameters(const BreakpointParameters &p);
int number; //!< Breakpoint number assigned by the debugger engine.
int subNumber; //!< Breakpoint sub-number assigned by the engine.
bool pending; //!< Breakpoint not fully resolved.
QString fullName; //!< Full file name acknowledged by the debugger engine.
bool multiple; //!< Happens in constructors/gdb.
......@@ -175,7 +206,15 @@ public:
typedef QList<BreakpointId> BreakpointIds;
inline uint qHash(const Debugger::Internal::BreakpointId &id)
{
return id.toInternalId();
}
} // namespace Internal
} // namespace Debugger
Q_DECLARE_METATYPE(Debugger::Internal::BreakpointId)
#endif // DEBUGGER_BREAKPOINT_H
......@@ -524,6 +524,7 @@ void BreakWindow::setModel(QAbstractItemModel *model)
resizeColumnToContents(0); // Number
resizeColumnToContents(3); // Line
resizeColumnToContents(6); // Ignore count
connect(model, SIGNAL(layoutChanged()), this, SLOT(expandAll()));
}
void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
......
......@@ -192,7 +192,7 @@ struct MemoryChangeCookie
struct ConditionalBreakPointCookie
{
ConditionalBreakPointCookie(BreakpointId i = 0) : id(i) {}
ConditionalBreakPointCookie(BreakpointId i = BreakpointId()) : id(i) {}
BreakpointId id;
GdbMi stopReason;
};
......@@ -1817,7 +1817,7 @@ static inline QString msgTracePointTriggered(BreakpointId id, const int number,
const QString &threadId)
{
return CdbEngine::tr("Trace point %1 (%2) in thread %3 triggered.")
.arg(id).arg(number).arg(threadId);
.arg(id.toString()).arg(number).arg(threadId);
}
static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const int number,
......@@ -1825,7 +1825,7 @@ static inline QString msgCheckingConditionalBreakPoint(BreakpointId id, const in
const QString &threadId)
{
return CdbEngine::tr("Conditional breakpoint %1 (%2) in thread %3 triggered, examining expression '%4'.")
.arg(id).arg(number).arg(threadId, QString::fromAscii(condition));
.arg(id.toString()).arg(number).arg(threadId, QString::fromAscii(condition));
}
unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
......@@ -1856,11 +1856,11 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
if (reason == "breakpoint") {
// Note: Internal breakpoints (run to line) are reported with id=0.
// Step out creates temporary breakpoints with id 10000.
BreakpointId id = 0;
BreakpointId id;
int number = 0;
const GdbMi breakpointIdG = stopReason.findChild("breakpointId");
if (breakpointIdG.isValid()) {
id = breakpointIdG.data().toULongLong();
id = BreakpointId(breakpointIdG.data().toInt());
if (id && breakHandler()->engineBreakpointIds(this).contains(id)) {
const BreakpointResponse parameters = breakHandler()->response(id);
// Trace point? Just report.
......@@ -1879,7 +1879,7 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
return StopReportLog;
}
} else {
id = 0;
id = BreakpointId();
}
}
QString tid = QString::number(threadId);
......@@ -2526,7 +2526,7 @@ void CdbEngine::attemptBreakpointSynchronization()
postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
}
if (!parameters.enabled)
postCommand("bd " + QByteArray::number(id), 0);
postCommand("bd " + QByteArray::number(id.majorPart()), 0);
handler->notifyBreakpointInsertProceeding(id);
handler->notifyBreakpointInsertOk(id);
m_pendingBreakpointMap.insert(id, response);
......@@ -2534,30 +2534,33 @@ void CdbEngine::attemptBreakpointSynchronization()
// Ensure enabled/disabled is correct in handler and line number is there.
handler->setResponse(id, response);
if (debugBreakpoints)
qDebug("Adding %llu %s\n", id, qPrintable(response.toString()));
qDebug("Adding %d %s\n", id.toInternalId(),
qPrintable(response.toString()));
break;
case BreakpointChangeRequested:
handler->notifyBreakpointChangeProceeding(id);
if (debugBreakpoints)
qDebug("Changing %llu:\n %s\nTo %s\n", id, qPrintable(handler->response(id).toString()),
qPrintable(parameters.toString()));
qDebug("Changing %d:\n %s\nTo %s\n", id.toInternalId(),
qPrintable(handler->response(id).toString()),
qPrintable(parameters.toString()));
if (parameters.enabled != handler->response(id).enabled) {
// Change enabled/disabled breakpoints without triggering update.
postCommand((parameters.enabled ? "be " : "bd ") + QByteArray::number(id), 0);
postCommand((parameters.enabled ? "be " : "bd ")
+ QByteArray::number(id.majorPart()), 0);
response.pending = false;
response.enabled = parameters.enabled;
handler->setResponse(id, response);
} else {
// Delete and re-add, triggering update
addedChanged = true;
postCommand("bc " + QByteArray::number(id), 0);
postCommand("bc " + QByteArray::number(id.majorPart()), 0);
postCommand(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false), 0);
m_pendingBreakpointMap.insert(id, response);
}
handler->notifyBreakpointChangeOk(id);
break;
case BreakpointRemoveRequested:
postCommand("bc " + QByteArray::number(id), 0);
postCommand("bc " + QByteArray::number(id.majorPart()), 0);
handler->notifyBreakpointRemoveProceeding(id);
handler->notifyBreakpointRemoveOk(id);
m_pendingBreakpointMap.remove(id);
......@@ -2689,9 +2692,9 @@ void CdbEngine::handleExpression(const CdbExtensionCommandPtr &command)
const ConditionalBreakPointCookie cookie = qvariant_cast<ConditionalBreakPointCookie>(command->cookie);
const QString message = value ?
tr("Value %1 obtained from evaluating the condition of breakpoint %2, stopping.").
arg(value).arg(cookie.id) :
arg(value).arg(cookie.id.toString()) :
tr("Value 0 obtained from evaluating the condition of breakpoint %1, continuing.").
arg(cookie.id);
arg(cookie.id.toString());
showMessage(message, LogMisc);
// Stop if evaluation is true, else continue
if (value) {
......@@ -2828,8 +2831,9 @@ void CdbEngine::handleBreakPoints(const GdbMi &value)
BreakpointResponse reportedResponse;
const BreakpointId id = parseBreakPoint(breakPointG, &reportedResponse);
if (debugBreakpoints)
qDebug(" Parsed %llu: pending=%d %s\n", id, reportedResponse.pending,
qPrintable(reportedResponse.toString()));
qDebug(" Parsed %d: pending=%d %s\n", id.majorPart(),
reportedResponse.pending,
qPrintable(reportedResponse.toString()));
if (!reportedResponse.pending) {
const PendingBreakPointMap::iterator it = m_pendingBreakpointMap.find(id);
......@@ -2843,7 +2847,8 @@ void CdbEngine::handleBreakPoints(const GdbMi &value)
currentResponse.enabled = reportedResponse.enabled;
formatCdbBreakPointResponse(id, currentResponse, str);
if (debugBreakpoints)
qDebug(" Setting for %llu: %s\n", id, qPrintable(currentResponse.toString()));
qDebug(" Setting for %d: %s\n", id.majorPart(),
qPrintable(currentResponse.toString()));
handler->setResponse(id, currentResponse);
m_pendingBreakpointMap.erase(it);
}
......
......@@ -135,7 +135,7 @@ static BreakpointParameters fixWinMSVCBreakpoint(const BreakpointParameters &p)
QByteArray cdbAddBreakpointCommand(const BreakpointParameters &bpIn,
const QList<QPair<QString, QString> > &sourcePathMapping,
BreakpointId id /* = BreakpointId(-1) */,
BreakpointId id /* = BreakpointId() */,
bool oneshot)
{
const BreakpointParameters bp = fixWinMSVCBreakpoint(bpIn);
......@@ -149,8 +149,8 @@ QByteArray cdbAddBreakpointCommand(const BreakpointParameters &bpIn,
// is kept when reporting back breakpoints (which is otherwise discarded
// when resolving).
str << (bp.type == WatchpointAtAddress ? "ba" : "bu");
if (id != BreakpointId(-1))
str << id;
if (id.isValid())
str << id.toString();
str << ' ';
if (oneshot)
str << "/1 ";
......@@ -308,7 +308,7 @@ BreakpointId parseBreakPoint(const GdbMi &gdbmi, BreakpointResponse *r,
const GdbMi idG = gdbmi.findChild("id");
if (idG.isValid()) { // Might not be valid if there is not id
bool ok;
const BreakpointId cid = idG.data().toULongLong(&ok);
const BreakpointId cid(idG.data().toInt(&ok));
if (ok)
id = cid;
}
......
......@@ -123,10 +123,11 @@ FORMS += attachexternaldialog.ui \
RESOURCES += debugger.qrc
false {
true {
SOURCES += $$PWD/modeltest.cpp
HEADERS += $$PWD/modeltest.h
DEFINES += USE_MODEL_TEST=1
#DEFINES += USE_WATCH_MODEL_TEST=1
#DEFINES += USE_BREAK_MODEL_TEST=1
}
win32 {
include(../../shared/registryaccess/registryaccess.pri)
......
......@@ -1497,7 +1497,7 @@ QString DebuggerEngine::msgWatchpointByExpressionTriggered(BreakpointId id,
{
return id
? tr("Data breakpoint %1 (%2) at %3 triggered.")
.arg(id).arg(number).arg(expr)
.arg(id.toString()).arg(number).arg(expr)
: tr("Internal data breakpoint %1 at %2 triggered.")
.arg(number).arg(expr);
}
......@@ -1507,7 +1507,7 @@ QString DebuggerEngine::msgWatchpointByExpressionTriggered(BreakpointId id,
{
return id
? tr("Data breakpoint %1 (%2) at %3 in thread %4 triggered.")
.arg(id).arg(number).arg(expr).arg(threadId)
.arg(id.toString()).arg(number).arg(expr).arg(threadId)
: tr("Internal data breakpoint %1 at %2 in thread %4 triggered.")