Commit 8ae541b3 authored by hjk's avatar hjk
Browse files

debugger: Refactor breakpoint handling.

The breakpoints are now (fairly) tightly guarded by the BreakpointHandler.
Engines and Views are only supposed to refer to them by id. They also have
individual states now. The breakpoint data is split into a "user requested"
"fixed" part in BreakpointData and the engines' acknowledged data in a new
struct BreakpointResponse.

TODO: Move m_state and m_engine members to BreakpointResponse. Fix regressions
in the marker handling.
parent 33bae0d7
This diff is collapsed.
......@@ -37,18 +37,15 @@
#include <QtGui/QIcon>
namespace Debugger {
class DebuggerEngine;
namespace Internal {
//////////////////////////////////////////////////////////////////
//
// BreakHandler
//
//////////////////////////////////////////////////////////////////
namespace Debugger {
namespace Internal {
class BreakHandler : public QAbstractTableModel
{
Q_OBJECT
......@@ -57,31 +54,28 @@ public:
BreakHandler();
~BreakHandler();
void removeAllBreakpoints();
void loadSessionData();
void saveSessionData();
QAbstractItemModel *model() { return this; }
BreakpointData *at(int index) const;
int size() const;
BreakpointIds allBreakpointIds() const;
BreakpointIds engineBreakpointIds(DebuggerEngine *engine) const;
BreakpointIds unclaimedBreakpointIds() const;
BreakpointData *breakpointById(BreakpointId id) const;
int size() const { return m_bp.size(); }
bool hasPendingBreakpoints() const;
void removeAt(int index); // This also deletes the marker.
void clear(); // This also deletes all the marker.
int indexOf(BreakpointData *data);
// Find a breakpoint matching approximately the data in needle.
BreakpointData *findSimilarBreakpoint(const BreakpointData *needle) const;
BreakpointData *findBreakpointByNumber(int bpNumber) const;
int findWatchPointIndexByAddress(quint64 address) const;
bool watchPointAt(quint64 address) const;
BreakpointId findSimilarBreakpoint(const BreakpointResponse &needle) const;
BreakpointId findBreakpointByNumber(int bpNumber) const;
BreakpointId findWatchpointByAddress(quint64 address) const;
BreakpointId findBreakpointByFunction(const QString &functionName) const;
BreakpointId findBreakpointByIndex(const QModelIndex &index) const;
void setWatchpointByAddress(quint64 address);
bool hasWatchpointAt(quint64 address) const;
void updateMarkers();
void updateMarker(BreakpointData *);
void removeMarker(BreakpointData *);
bool isActive() const;
Breakpoints takeRemovedBreakpoints(); // Owned.
Breakpoints takeEnabledBreakpoints(); // Not owned.
Breakpoints takeDisabledBreakpoints(); // Not owned.
void removeMarker(BreakpointId id);
QIcon breakpointIcon() const { return m_breakpointIcon; }
QIcon disabledBreakpointIcon() const { return m_disabledBreakpointIcon; }
......@@ -89,21 +83,60 @@ public:
QIcon emptyIcon() const { return m_emptyIcon; }
void toggleBreakpoint(const QString &fileName, int lineNumber, quint64 address = 0);
void toggleBreakpointEnabled(const QString &fileName, int lineNumber);
BreakpointData *findBreakpoint(const QString &fileName, int lineNumber,
bool useMarkerPosition = true);
BreakpointData *findBreakpoint(quint64 address) const;
BreakpointId findBreakpointByFileAndLine(const QString &fileName,
int lineNumber, bool useMarkerPosition = true);
BreakpointId findBreakpointByAddress(quint64 address) const;
void appendBreakpoint(BreakpointData *data);
void reinsertBreakpoint(BreakpointData *data);
void toggleBreakpointEnabled(BreakpointData *data);
void breakByFunction(const QString &functionName);
void removeBreakpoint(int index);
void removeBreakpoint(BreakpointData *data);
void synchronizeBreakpoints();
signals:
void breakpointSynchronizationRequested();
void removeBreakpoint(BreakpointId id);
QIcon icon(BreakpointId id) const;
void gotoLocation(BreakpointId id) const;
// Getter retrieves property value.
// Setter sets property value and triggers update if changed.
#define PROPERTY(type, getter, setter) \
type getter(BreakpointId id) const; \
void setter(BreakpointId id, const type &value);
PROPERTY(bool, useFullPath, setUseFullPath)
PROPERTY(QString, markerFileName, setMarkerFileName)
PROPERTY(int, markerLineNumber, setMarkerLineNumber)
PROPERTY(QByteArray, condition, setCondition)
PROPERTY(int, ignoreCount, setIgnoreCount)
PROPERTY(QByteArray, threadSpec, setThreadSpec)
PROPERTY(BreakpointState, state, setState)
PROPERTY(QString, fileName, setFileName)
PROPERTY(QString, functionName, setFunctionName)
PROPERTY(BreakpointType, type, setType);
PROPERTY(quint64, address, setAddress);
PROPERTY(int, lineNumber, setLineNumber);
#undef PROPERTY
bool isEnabled(BreakpointId id) const;
void setEnabled(BreakpointId id, const bool &on);
void updateEnabled(BreakpointId id, const bool &on);
void updateLineNumberFromMarker(BreakpointId id, int lineNumber);
DebuggerEngine *engine(BreakpointId id) const;
void setEngine(BreakpointId id, DebuggerEngine *engine);
BreakpointResponse response(BreakpointId id) const;
void setResponse(BreakpointId id, const BreakpointResponse &data);
// Incorporate debugger feedback. No synchronization request needed.
// Return true if something changed.
void ackCondition(BreakpointId id);
void ackIgnoreCount(BreakpointId id);
void notifyBreakpointInsertOk(BreakpointId id);
void notifyBreakpointInsertFailed(BreakpointId id);
void notifyBreakpointChangeOk(BreakpointId id);
void notifyBreakpointChangeFailed(BreakpointId id);
void notifyBreakpointRemoveOk(BreakpointId id);
void notifyBreakpointRemoveFailed(BreakpointId id);
void notifyBreakpointReleased(BreakpointId id);
// This takes ownership.
void appendBreakpoint(BreakpointData *data);
private:
friend class BreakpointMarker;
......@@ -118,8 +151,8 @@ private:
void markerUpdated(BreakpointMarker *marker, int lineNumber);
void loadBreakpoints();
void saveBreakpoints();
void removeBreakpointHelper(int index);
void append(BreakpointData *data);
void updateMarker(BreakpointId id);
void cleanupBreakpoint(BreakpointId id);
const QIcon m_breakpointIcon;
const QIcon m_disabledBreakpointIcon;
......@@ -127,17 +160,17 @@ private:
const QIcon m_emptyIcon;
const QIcon m_watchpointIcon;
Breakpoints m_inserted; // Lately inserted breakpoints.
Breakpoints m_removed; // Lately removed breakpoints.
Breakpoints m_enabled; // Lately enabled breakpoints.
Breakpoints m_disabled; // Lately disabled breakpoints.
// Hack for BreakWindow::findSimilarBreakpoint
mutable BreakpointData *m_lastFound;
mutable bool m_lastFoundQueried;
typedef QMap<BreakpointId, BreakpointData *> Breakpoints;
typedef QMap<BreakpointId, BreakpointMarker *> Markers;
typedef QMap<BreakpointId, BreakpointResponse *> Responses;
typedef Breakpoints::ConstIterator ConstIterator;
Breakpoints m_bp;
QHash<quint64, BreakpointMarker *> m_markers;
Markers m_markers;
Responses m_responses;
void scheduleSynchronization();
void timerEvent(QTimerEvent *event);
int m_syncTimerId;
};
} // namespace Internal
......
......@@ -50,88 +50,86 @@ namespace Internal {
const char *BreakpointData::throwFunction = "throw";
const char *BreakpointData::catchFunction = "catch";
static quint64 nextBPId() {
/* ok to be not thread-safe
the order does not matter and only the gui
produces authoritative ids. well, for now...
*/
static quint64 i=1;
return ++i;
}
BreakpointData::BreakpointData() :
id(nextBPId()), uiDirty(false), enabled(true),
pending(true), type(BreakpointByFileAndLine),
ignoreCount(0), lineNumber(0), address(0),
useFullPath(false),
bpIgnoreCount(0), bpLineNumber(0),
bpCorrectedLineNumber(0), bpAddress(0),
bpMultiple(false), bpEnabled(true),
m_markerLineNumber(0)
{
}
BreakpointData *BreakpointData::clone() const
static quint64 nextBPId()
{
BreakpointData *data = new BreakpointData();
data->enabled = enabled;
data->type = type;
data->fileName = fileName;
data->condition = condition;
data->ignoreCount = ignoreCount;
data->lineNumber = lineNumber;
data->address = address;
data->threadSpec = threadSpec;
data->funcName = funcName;
data->useFullPath = useFullPath;
if (data->type == BreakpointByFunction) {
// FIXME: Would removing it be better then leaving this
// "history" around?
data->m_markerFileName = m_markerFileName;
data->m_markerLineNumber = m_markerLineNumber;
} else {
data->m_markerFileName = fileName;
data->m_markerLineNumber = lineNumber;
}
return data;
// Ok to be not thread-safe. The order does not matter and only the gui
// produces authoritative ids.
static quint64 i = 0;
return ++i;
}
BreakpointData::~BreakpointData()
BreakpointData::BreakpointData()
{
m_state = BreakpointNew;
m_engine = 0;
m_enabled = true;
m_type = BreakpointByFileAndLine;
m_ignoreCount = 0;
m_lineNumber = 0;
m_address = 0;
m_useFullPath = false;
m_markerLineNumber = 0;
}
void BreakpointData::clear()
BreakpointResponse::BreakpointResponse()
{
uiDirty = false;
pending = true;
bpNumber.clear();
bpCondition.clear();
bpNumber = 0;
bpIgnoreCount = 0;
bpFileName.clear();
bpFullName.clear();
bpLineNumber = 0;
bpCorrectedLineNumber = 0;
bpThreadSpec.clear();
bpFuncName.clear();
//bpCorrectedLineNumber = 0;
bpAddress = 0;
bpMultiple = false;
bpEnabled = true;
bpState.clear();
m_markerFileName = fileName;
m_markerLineNumber = lineNumber;
}
void BreakpointData::setMarkerFileName(const QString &fileName)
{
m_markerFileName = fileName;
}
#define SETIT(var, value) return (var != value) && (var = value, true)
void BreakpointData::setMarkerLineNumber(int lineNumber)
{
m_markerLineNumber = lineNumber;
}
bool BreakpointData::setUseFullPath(bool on)
{ SETIT(m_useFullPath, on); }
bool BreakpointData::setMarkerFileName(const QString &file)
{ SETIT(m_markerFileName, file); }
bool BreakpointData::setMarkerLineNumber(int line)
{ SETIT(m_markerLineNumber, line); }
bool BreakpointData::setFileName(const QString &file)
{ SETIT(m_fileName, file); }
bool BreakpointData::setEnabled(bool on)
{ SETIT(m_enabled, on); }
bool BreakpointData::setIgnoreCount(bool count)
{ SETIT(m_ignoreCount, count); }
bool BreakpointData::setFunctionName(const QString &name)
{ SETIT(m_functionName, name); }
bool BreakpointData::setLineNumber(int line)
{ SETIT(m_lineNumber, line); }
bool BreakpointData::setAddress(quint64 address)
{ SETIT(m_address, address); }
static inline void formatAddress(QTextStream &str, quint64 address)
bool BreakpointData::setThreadSpec(const QByteArray &spec)
{ SETIT(m_threadSpec, spec); }
bool BreakpointData::setType(BreakpointType type)
{ SETIT(m_type, type); }
bool BreakpointData::setCondition(const QByteArray &cond)
{ SETIT(m_condition, cond); }
bool BreakpointData::setState(BreakpointState state)
{ SETIT(m_state, state); }
bool BreakpointData::setEngine(DebuggerEngine *engine)
{ SETIT(m_engine, engine); }
#undef SETIT
static void formatAddress(QTextStream &str, quint64 address)
{
if (address) {
str << "0x";
......@@ -143,8 +141,8 @@ static inline void formatAddress(QTextStream &str, quint64 address)
QString BreakpointData::toToolTip() const
{
QString t = tr("Unknown Breakpoint Type");
switch (type) {
QString t;
switch (m_type) {
case BreakpointByFileAndLine:
t = tr("Breakpoint by File and Line");
break;
......@@ -157,64 +155,72 @@ QString BreakpointData::toToolTip() const
case Watchpoint:
t = tr("Watchpoint");
break;
case UnknownType:
t = tr("Unknown Breakpoint Type");
}
QString rc;
QTextStream str(&rc);
str << "<html><body><table>"
//<< "<tr><td>" << tr("Id:")
//<< "</td><td>" << m_id << "</td></tr>"
<< "<tr><td>" << tr("State:")
<< "</td><td>" << m_state << "</td></tr>"
<< "<tr><td>" << tr("Engine:")
<< "</td><td>" << m_engine << "</td></tr>"
<< "<tr><td>" << tr("Marker File:")
<< "</td><td>" << QDir::toNativeSeparators(m_markerFileName) << "</td></tr>"
<< "<tr><td>" << tr("Marker Line:")
<< "</td><td>" << m_markerLineNumber << "</td></tr>"
<< "<tr><td>" << tr("Breakpoint Number:")
<< "</td><td>" << bpNumber << "</td></tr>"
//<< "<tr><td>" << tr("Breakpoint Number:")
//<< "</td><td>" << bpNumber << "</td></tr>"
<< "<tr><td>" << tr("Breakpoint Type:")
<< "</td><td>" << t << "</td></tr>"
<< "<tr><td>" << tr("State:")
<< "</td><td>" << bpState << "</td></tr>"
//<< "</td><td>" << bpState << "</td></tr>"
<< "</table><br><hr><table>"
<< "<tr><th>" << tr("Property")
<< "</th><th>" << tr("Requested")
<< "</th><th>" << tr("Obtained") << "</th></tr>"
<< "<tr><td>" << tr("Internal Number:")
<< "</td><td>&mdash;</td><td>" << bpNumber << "</td></tr>"
//<< "</td><td>&mdash;</td><td>" << bpNumber << "</td></tr>"
<< "<tr><td>" << tr("File Name:")
<< "</td><td>" << QDir::toNativeSeparators(fileName)
<< "</td><td>" << QDir::toNativeSeparators(bpFileName) << "</td></tr>"
<< "</td><td>" << QDir::toNativeSeparators(m_fileName)
//<< "</td><td>" << QDir::toNativeSeparators(bpFileName) << "</td></tr>"
<< "<tr><td>" << tr("Function Name:")
<< "</td><td>" << funcName << "</td><td>" << bpFuncName << "</td></tr>"
<< "</td><td>" << m_functionName // << "</td><td>" << bpFuncName << "</td></tr>"
<< "<tr><td>" << tr("Line Number:") << "</td><td>";
if (lineNumber)
str << lineNumber;
str << "</td><td>";
if (bpLineNumber)
str << bpLineNumber;
if (m_lineNumber)
str << m_lineNumber;
//str << "</td><td>";
//if (bpLineNumber)
// str << bpLineNumber;
str << "</td></tr>"
<< "<tr><td>" << tr("Breakpoint Address:")
<< "</td><td>";
formatAddress(str, address);
formatAddress(str, m_address);
str << "</td><td>";
formatAddress(str, bpAddress);
str << "</td></tr>"
<< "<tr><td>" << tr("Corrected Line Number:")
<< "</td><td>-</td><td>";
if (bpCorrectedLineNumber > 0) {
str << bpCorrectedLineNumber;
} else {
str << '-';
}
//formatAddress(str, bpAddress);
//str << "</td></tr>"
// << "<tr><td>" << tr("Corrected Line Number:")
// << "</td><td>-</td><td>";
//if (bpCorrectedLineNumber > 0) {
// str << bpCorrectedLineNumber;
// } else {
// str << '-';
// }
str << "</td></tr>"
<< "<tr><td>" << tr("Condition:")
<< "</td><td>" << condition << "</td><td>" << bpCondition << "</td></tr>"
// << "</td><td>" << m_condition << "</td><td>" << bpCondition << "</td></tr>"
<< "<tr><td>" << tr("Ignore Count:") << "</td><td>";
if (ignoreCount)
str << ignoreCount;
if (m_ignoreCount)
str << m_ignoreCount;
str << "</td><td>";
if (bpIgnoreCount)
str << bpIgnoreCount;
//if (bpIgnoreCount)
// str << bpIgnoreCount;
str << "</td></tr>"
<< "<tr><td>" << tr("Thread Specification:")
<< "</td><td>" << threadSpec << "</td><td>" << bpThreadSpec << "</td></tr>"
// << "</td><td>" << m_threadSpec << "</td><td>" << bpThreadSpec << "</td></tr>"
<< "</table></body></html>";
return rc;
}
......@@ -229,66 +235,52 @@ static inline bool fileNameMatch(const QString &f1, const QString &f2)
#endif
}
bool BreakpointData::isLocatedAt(const QString &fileName_, int lineNumber_,
bool BreakpointData::isLocatedAt(const QString &fileName, int lineNumber,
bool useMarkerPosition) const
{
int line = useMarkerPosition ? m_markerLineNumber : lineNumber;
return lineNumber_ == line && fileNameMatch(fileName_, m_markerFileName);
int line = useMarkerPosition ? m_markerLineNumber : m_lineNumber;
return lineNumber == line && fileNameMatch(fileName, m_markerFileName);
}
bool BreakpointData::isSimilarTo(const BreakpointData *needle) const
{
//qDebug() << "COMPARING " << toString() << " WITH " << needle->toString();
if (id == needle->id && id != 0)
return true;
// Clear hit.
if (bpNumber == needle->bpNumber
&& !bpNumber.isEmpty()
&& bpNumber.toInt() != 0)
return true;
// Clear miss.
if (type != needle->type)
return false;
// We have numbers, but they are different.
if (!bpNumber.isEmpty() && !needle->bpNumber.isEmpty()
&& !bpNumber.startsWith(needle->bpNumber)
&& !needle->bpNumber.startsWith(bpNumber))
return false;
if (address && needle->address && address == needle->address)
return true;
// At least at a position we were looking for.
// FIXME: breaks multiple breakpoints at the same location
if (!fileName.isEmpty()
&& fileNameMatch(fileName, needle->fileName)
&& lineNumber == needle->lineNumber)
return true;
// At least at a position we were looking for.
// FIXME: breaks multiple breakpoints at the same location
if (!fileName.isEmpty()
&& fileNameMatch(fileName, needle->bpFileName)
&& lineNumber == needle->bpLineNumber)
return true;
return false;
}
bool BreakpointData::conditionsMatch() const
bool BreakpointData::conditionsMatch(const QString &other) const
{
// Some versions of gdb "beautify" the passed condition.
QString s1 = condition;
QString s1 = m_condition;
s1.remove(QChar(' '));
QString s2 = bpCondition;
QString s2 = other;
s2.remove(QChar(' '));
return s1 == s2;
}
QString BreakpointData::toString() const
{
QString result;
QTextStream ts(&result);
ts << fileName();
ts << condition();
ts << ignoreCount();
ts << lineNumber();
ts << address();
ts << functionName();
ts << useFullPath();
return result;
}
QString BreakpointResponse::toString() const
{
QString result;
QTextStream ts(&result);
ts << bpNumber;
ts << bpCondition;
ts << bpIgnoreCount;
ts << bpFileName;
ts << bpFullName;
ts << bpLineNumber;
ts << bpThreadSpec;
ts << bpFuncName;
ts << bpAddress;
return result;
}
} // namespace Internal
} // namespace Debugger
......@@ -36,6 +36,11 @@
#include <QtCore/QCoreApplication>
namespace Debugger {
class DebuggerEngine;
typedef quint64 BreakpointId; // FIXME: make Internal.
namespace Internal {
class BreakpointMarker;
......@@ -49,6 +54,7 @@ class BreakHandler;
enum BreakpointType
{
UnknownType,
BreakpointByFileAndLine,
BreakpointByFunction,
BreakpointByAddress,
......@@ -58,36 +64,21 @@ enum BreakpointType
enum BreakpointState
{
BreakpointNew,
BreakpointInsertionRequested, // Inferior was told about bp, not ack'ed.
BreakpointInsertRequested, // Inferior was told about bp, not ack'ed.
BreakpointInsertProceeding,
BreakpointChangeRequested,
BreakpointOk,
BreakpointRemovalRequested,
BreakpointRemoved,
BreakpointChangeProceeding,
BreakpointPending,
BreakpointInserted,
BreakpointRemoveRequested,
BreakpointRemoveProceeding,
BreakpointDead,
};
class BreakpointData
{
public:
BreakpointData();
~BreakpointData();
QString toToolTip() const;
void clear(); // Delete all generated data.
bool isLocatedAt(const QString &fileName, int lineNumber,
bool useMarkerPosition) const;
bool isSimilarTo(const BreakpointData *needle) const;
bool conditionsMatch() const;
// This copies only the static data.
BreakpointData *clone() const;