debugger: work on watchpoints (or, "data breakpoints" as they are called now)

This commit is contained in:
hjk
2011-05-10 15:57:33 +02:00
parent 6331b68943
commit e8496ca33f
11 changed files with 96 additions and 56 deletions

View File

@@ -287,6 +287,7 @@ BreakpointId BreakHandler::findWatchpoint(const BreakpointParameters &data) cons
if (it->data.isWatchpoint() if (it->data.isWatchpoint()
&& it->data.address == data.address && it->data.address == data.address
&& it->data.size == data.size && it->data.size == data.size
&& it->data.expression == data.expression
&& it->data.bitpos == data.bitpos) && it->data.bitpos == data.bitpos)
return it.key(); return it.key();
return BreakpointId(); return BreakpointId();
@@ -520,7 +521,9 @@ QVariant BreakHandler::data(const QModelIndex &mi, int role) const
|| data.type == BreakpointAtSysCall) || data.type == BreakpointAtSysCall)
return typeToString(data.type); return typeToString(data.type);
if (data.type == WatchpointAtAddress) if (data.type == WatchpointAtAddress)
return tr("Watchpoint at 0x%1").arg(data.address, 0, 16); return tr("Data breakpoint at 0x%1").arg(data.address, 0, 16);
if (data.type == WatchpointAtExpression)
return tr("Data breakpoint at %1").arg(data.expression);
return empty; return empty;
} }
break; break;
@@ -650,7 +653,7 @@ PROPERTY(int, threadSpec, setThreadSpec)
PROPERTY(QByteArray, condition, setCondition) PROPERTY(QByteArray, condition, setCondition)
GETTER(int, lineNumber) GETTER(int, lineNumber)
PROPERTY(quint64, address, setAddress) PROPERTY(quint64, address, setAddress)
PROPERTY(QByteArray, expression, setExpression) PROPERTY(QString, expression, setExpression)
PROPERTY(int, ignoreCount, setIgnoreCount) PROPERTY(int, ignoreCount, setIgnoreCount)
bool BreakHandler::isEnabled(BreakpointId id) const bool BreakHandler::isEnabled(BreakpointId id) const
@@ -1207,6 +1210,8 @@ QIcon BreakHandler::BreakpointItem::icon() const
return BreakHandler::tracepointIcon(); return BreakHandler::tracepointIcon();
if (data.type == WatchpointAtAddress) if (data.type == WatchpointAtAddress)
return BreakHandler::watchpointIcon(); return BreakHandler::watchpointIcon();
if (data.type == WatchpointAtExpression)
return BreakHandler::watchpointIcon();
if (!data.enabled) if (!data.enabled)
return BreakHandler::disabledBreakpointIcon(); return BreakHandler::disabledBreakpointIcon();
if (state == BreakpointInserted) if (state == BreakpointInserted)

View File

@@ -115,8 +115,8 @@ public:
void setFileName(BreakpointId, const QString &fileName); void setFileName(BreakpointId, const QString &fileName);
QString functionName(BreakpointId id) const; QString functionName(BreakpointId id) const;
void setFunctionName(BreakpointId, const QString &functionName); void setFunctionName(BreakpointId, const QString &functionName);
QByteArray expression(BreakpointId id) const; QString expression(BreakpointId id) const;
void setExpression(BreakpointId, const QByteArray &expression); void setExpression(BreakpointId, const QString &expression);
BreakpointType type(BreakpointId id) const; BreakpointType type(BreakpointId id) const;
void setType(BreakpointId id, const BreakpointType &type); void setType(BreakpointId id, const BreakpointType &type);
quint64 address(BreakpointId id) const; quint64 address(BreakpointId id) const;

View File

@@ -144,7 +144,7 @@ public:
int ignoreCount; //!< Ignore count associated with breakpoint. int ignoreCount; //!< Ignore count associated with breakpoint.
int lineNumber; //!< Line in source file. int lineNumber; //!< Line in source file.
quint64 address; //!< Address for address based watchpoints. quint64 address; //!< Address for address based watchpoints.
QByteArray expression; //!< Address for expression based watchpoints. QString expression; //!< Expression for expression based watchpoints.
uint size; //!< Size of watched area for watchpoints. uint size; //!< Size of watched area for watchpoints.
uint bitpos; //!< Location of watched bitfield within watched area. uint bitpos; //!< Location of watched bitfield within watched area.
uint bitsize; //!< Size of watched bitfield within watched area. uint bitsize; //!< Size of watched bitfield within watched area.

View File

@@ -111,15 +111,15 @@ BreakpointDialog::BreakpointDialog(unsigned engineCapabilities, QWidget *parent)
QStringList types; QStringList types;
types << tr("File name and line number") types << tr("File name and line number")
<< tr("Function name") << tr("Function name")
<< tr("Memory address") << tr("Break on memory address")
<< tr("Break when C++ exception is thrown") << tr("Break when C++ exception is thrown")
<< tr("Break when C++ exception is caught") << tr("Break when C++ exception is caught")
<< tr("Break when function \"main\" starts") << tr("Break when function \"main\" starts")
<< tr("Break when a new process is forked") << tr("Break when a new process is forked")
<< tr("Break when a new process is executed") << tr("Break when a new process is executed")
<< tr("Break when a system call is executed") << tr("Break when a system call is executed")
<< tr("Break on data access (Watchpoint at address)") << tr("Break on data access at fixed address)")
<< tr("Break on data access (Watchpoint at expression)"); << tr("Break on data access at address given by expression)");
QTC_ASSERT(types.size() == WatchpointAtExpression, return; ) QTC_ASSERT(types.size() == WatchpointAtExpression, return; )
m_ui.comboBoxType->addItems(types); m_ui.comboBoxType->addItems(types);
m_ui.pathChooserFileName->setExpectedKind(Utils::PathChooser::File); m_ui.pathChooserFileName->setExpectedKind(Utils::PathChooser::File);

View File

@@ -1489,32 +1489,32 @@ bool DebuggerEngine::isDying() const
} }
QString DebuggerEngine::msgWatchpointByExpressionTriggered(BreakpointId id, QString DebuggerEngine::msgWatchpointByExpressionTriggered(BreakpointId id,
const int number, const QByteArray &expr) const int number, const QString &expr)
{ {
return id return id
? tr("Watchpoint %1 (%2) at %3 %4 triggered.") ? tr("Data breakpoint %1 (%2) at %3 triggered.")
.arg(id).arg(number).arg(_(expr)) .arg(id).arg(number).arg(expr)
: tr("Internal watchpoint %1 at %2 %4 triggered.") : tr("Internal data breakpoint %1 at %2 triggered.")
.arg(number).arg(_(expr)); .arg(number).arg(expr);
} }
QString DebuggerEngine::msgWatchpointByExpressionTriggered(BreakpointId id, QString DebuggerEngine::msgWatchpointByExpressionTriggered(BreakpointId id,
const int number, const QByteArray &expr, const QString &threadId) const int number, const QString &expr, const QString &threadId)
{ {
return id return id
? tr("Watchpoint %1 (%2) at %3 in thread %4 triggered.") ? tr("Data breakpoint %1 (%2) at %3 in thread %4 triggered.")
.arg(id).arg(number).arg(_(expr)).arg(threadId) .arg(id).arg(number).arg(expr).arg(threadId)
: tr("Internal watchpoint %1 at %2 in thread %4 triggered.") : tr("Internal data breakpoint %1 at %2 in thread %4 triggered.")
.arg(number).arg(_(expr)).arg(threadId); .arg(number).arg(expr).arg(threadId);
} }
QString DebuggerEngine::msgWatchpointByAddressTriggered(BreakpointId id, QString DebuggerEngine::msgWatchpointByAddressTriggered(BreakpointId id,
const int number, quint64 address) const int number, quint64 address)
{ {
return id return id
? tr("Watchpoint %1 (%2) at 0x%3 triggered.") ? tr("Data breakpoint %1 (%2) at 0x%3 triggered.")
.arg(id).arg(number).arg(address, 0, 16) .arg(id).arg(number).arg(address, 0, 16)
: tr("Internal watchpoint %1 at 0x%2 triggered.") : tr("Internal data breakpoint %1 at 0x%2 triggered.")
.arg(number).arg(address, 0, 16); .arg(number).arg(address, 0, 16);
} }
@@ -1522,9 +1522,9 @@ QString DebuggerEngine::msgWatchpointByAddressTriggered(BreakpointId id,
const int number, quint64 address, const QString &threadId) const int number, quint64 address, const QString &threadId)
{ {
return id return id
? tr("Watchpoint %1 (%2) at 0x%3 in thread %4 triggered.") ? tr("Data breakpoint %1 (%2) at 0x%3 in thread %4 triggered.")
.arg(id).arg(number).arg(address, 0, 16).arg(threadId) .arg(id).arg(number).arg(address, 0, 16).arg(threadId)
: tr("Internal watchpoint %1 at 0x%2 in thread %3 triggered.") : tr("Internal data breakpoint %1 at 0x%2 in thread %3 triggered.")
.arg(id).arg(number).arg(address, 0, 16).arg(threadId); .arg(id).arg(number).arg(address, 0, 16).arg(threadId);
} }

View File

@@ -364,9 +364,9 @@ protected:
static QString msgWatchpointByAddressTriggered(BreakpointId id, static QString msgWatchpointByAddressTriggered(BreakpointId id,
int number, quint64 address, const QString &threadId); int number, quint64 address, const QString &threadId);
static QString msgWatchpointByExpressionTriggered(BreakpointId id, static QString msgWatchpointByExpressionTriggered(BreakpointId id,
int number, const QByteArray &expr); int number, const QString &expr);
static QString msgWatchpointByExpressionTriggered(BreakpointId id, static QString msgWatchpointByExpressionTriggered(BreakpointId id,
int number, const QByteArray &expr, const QString &threadId); int number, const QString &expr, const QString &threadId);
static QString msgBreakpointTriggered(BreakpointId id, static QString msgBreakpointTriggered(BreakpointId id,
int number, const QString &threadId); int number, const QString &threadId);
static QString msgStopped(const QString &reason = QString()); static QString msgStopped(const QString &reason = QString());

View File

@@ -83,6 +83,7 @@ enum ModelRoles
LocalsINameRole, LocalsINameRole,
LocalsEditTypeRole, // A QVariant::type describing the item LocalsEditTypeRole, // A QVariant::type describing the item
LocalsIntegerBaseRole, // Number base 16, 10, 8, 2 LocalsIntegerBaseRole, // Number base 16, 10, 8, 2
LocalsNameRole,
LocalsExpressionRole, LocalsExpressionRole,
LocalsRawExpressionRole, LocalsRawExpressionRole,
LocalsExpandedRole, // The preferred expanded state to the view LocalsExpandedRole, // The preferred expanded state to the view

View File

@@ -2706,7 +2706,7 @@ void GdbEngine::insertBreakpoint(BreakpointId id)
return; return;
} }
if (type == WatchpointAtExpression) { if (type == WatchpointAtExpression) {
postCommand("watch " + handler->expression(id), postCommand("watch " + handler->expression(id).toLocal8Bit(),
NeedsStop | RebuildBreakpointModel, NeedsStop | RebuildBreakpointModel,
CB(handleWatchInsert), id); CB(handleWatchInsert), id);
return; return;

View File

@@ -605,6 +605,9 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
case LocalsEditTypeRole: case LocalsEditTypeRole:
return QVariant(editType(data)); return QVariant(editType(data));
case LocalsNameRole:
return QVariant(data.name);
case LocalsIntegerBaseRole: case LocalsIntegerBaseRole:
if (isPointerType(data.type)) // Pointers using 0x-convention if (isPointerType(data.type)) // Pointers using 0x-convention
return QVariant(16); return QVariant(16);

View File

@@ -467,7 +467,7 @@ WatchWindow::WatchWindow(Type type, QWidget *parent)
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
setAttribute(Qt::WA_MacShowFocusRect, false); setAttribute(Qt::WA_MacShowFocusRect, false);
setWindowTitle(tr("Locals and Watchers")); setWindowTitle(tr("Locals and Expressions"));
setIndentation(indentation() * 9/10); setIndentation(indentation() * 9/10);
setUniformRowHeights(true); setUniformRowHeights(true);
setItemDelegate(new WatchDelegate(this)); setItemDelegate(new WatchDelegate(this));
@@ -562,24 +562,24 @@ void WatchWindow::mouseDoubleClickEvent(QMouseEvent *ev)
static inline QString addWatchActionText(QString exp) static inline QString addWatchActionText(QString exp)
{ {
if (exp.isEmpty()) if (exp.isEmpty())
return WatchWindow::tr("Watch Expression"); return WatchWindow::tr("Evaluate Expression");
if (exp.size() > 30) { if (exp.size() > 30) {
exp.truncate(30); exp.truncate(30);
exp.append(QLatin1String("...")); exp.append(QLatin1String("..."));
} }
return WatchWindow::tr("Watch Expression \"%1\"").arg(exp); return WatchWindow::tr("Evaluate Expression \"%1\"").arg(exp);
} }
// Text for add watch action with truncated expression // Text for add watch action with truncated expression
static inline QString removeWatchActionText(QString exp) static inline QString removeWatchActionText(QString exp)
{ {
if (exp.isEmpty()) if (exp.isEmpty())
return WatchWindow::tr("Remove Watch Expression"); return WatchWindow::tr("Remove Evaluated Expression");
if (exp.size() > 30) { if (exp.size() > 30) {
exp.truncate(30); exp.truncate(30);
exp.append(QLatin1String("...")); exp.append(QLatin1String("..."));
} }
return WatchWindow::tr("Remove Watch Expression \"%1\"").arg(exp); return WatchWindow::tr("Remove Evaluated Expression \"%1\"").arg(exp);
} }
static inline void copyToClipboard(const QString &clipboardText) static inline void copyToClipboard(const QString &clipboardText)
@@ -604,8 +604,12 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
const uint size = sizeOf(mi0); const uint size = sizeOf(mi0);
const quint64 pointerValue = pointerValueOf(mi0); const quint64 pointerValue = pointerValueOf(mi0);
const QString exp = mi0.data(LocalsExpressionRole).toString(); const QString exp = mi0.data(LocalsExpressionRole).toString();
const QString name = mi0.data(LocalsNameRole).toString();
const QString type = mi2.data().toString(); const QString type = mi2.data().toString();
// Offer to open address pointed to or variable address.
const bool createPointerActions = pointerValue && pointerValue != address;
const QStringList alternativeFormats = const QStringList alternativeFormats =
mi0.data(LocalsTypeFormatListRole).toStringList(); mi0.data(LocalsTypeFormatListRole).toStringList();
const int typeFormat = const int typeFormat =
@@ -686,44 +690,57 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
const unsigned engineCapabilities = engine->debuggerCapabilities(); const unsigned engineCapabilities = engine->debuggerCapabilities();
const bool canHandleWatches = engineCapabilities & AddWatcherCapability; const bool canHandleWatches = engineCapabilities & AddWatcherCapability;
const DebuggerState state = engine->state(); const DebuggerState state = engine->state();
const bool canInsertWatches = (state==InferiorStopOk) || ((state==InferiorRunOk) && engine->acceptsWatchesWhileRunning()); const bool canInsertWatches = state == InferiorStopOk
|| (state == InferiorRunOk && engine->acceptsWatchesWhileRunning());
QMenu menu;
QAction *actInsertNewWatchItem = menu.addAction(tr("Insert New Watch Item"));
actInsertNewWatchItem->setEnabled(canHandleWatches && canInsertWatches);
QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
actSelectWidgetToWatch->setEnabled(canHandleWatches && (engine->canWatchWidgets()));
// Offer to open address pointed to or variable address.
const bool createPointerActions = pointerValue && pointerValue != address;
menu.addSeparator();
QMenu breakpointMenu;
breakpointMenu.setTitle(tr("Add Data Breakpoint..."));
QAction *actSetWatchpointAtVariableAddress = 0; QAction *actSetWatchpointAtVariableAddress = 0;
QAction *actSetWatchpointAtPointerValue = 0; QAction *actSetWatchpointAtPointerValue = 0;
const bool canSetWatchpoint = engineCapabilities & WatchpointByAddressCapability; const bool canSetWatchpoint = engineCapabilities & WatchpointByAddressCapability;
if (canSetWatchpoint && address) { if (canSetWatchpoint && address) {
actSetWatchpointAtVariableAddress = actSetWatchpointAtVariableAddress =
new QAction(tr("Add Watchpoint at Object's Address (0x%1)") new QAction(tr("Add Data Breakpoint at Object's Address (0x%1)")
.arg(address, 0, 16), &menu); .arg(address, 0, 16), &breakpointMenu);
actSetWatchpointAtVariableAddress-> actSetWatchpointAtVariableAddress->
setChecked(mi0.data(LocalsIsWatchpointAtAddressRole).toBool()); setChecked(mi0.data(LocalsIsWatchpointAtAddressRole).toBool());
if (createPointerActions) { if (createPointerActions) {
actSetWatchpointAtPointerValue = actSetWatchpointAtPointerValue =
new QAction(tr("Add Watchpoint at Referenced Address (0x%1)") new QAction(tr("Add Data Breakpoint at Referenced Address (0x%1)")
.arg(pointerValue, 0, 16), &menu); .arg(pointerValue, 0, 16), &breakpointMenu);
actSetWatchpointAtPointerValue->setCheckable(true); actSetWatchpointAtPointerValue->setCheckable(true);
actSetWatchpointAtPointerValue-> actSetWatchpointAtPointerValue->
setChecked(mi0.data(LocalsIsWatchpointAtPointerValueRole).toBool()); setChecked(mi0.data(LocalsIsWatchpointAtPointerValueRole).toBool());
} }
} else { } else {
actSetWatchpointAtVariableAddress = actSetWatchpointAtVariableAddress =
new QAction(tr("Add Watchpoint"), &menu); new QAction(tr("Add Data Breakpoint"), &breakpointMenu);
actSetWatchpointAtVariableAddress->setEnabled(false); actSetWatchpointAtVariableAddress->setEnabled(false);
} }
actSetWatchpointAtVariableAddress->setToolTip( actSetWatchpointAtVariableAddress->setToolTip(
tr("Setting a watchpoint on an address will cause the program " tr("Setting a data breakpoint on an address will cause the program "
"to stop when the data at the address it modified.")); "to stop when the data at the address is modified."));
QAction *actSetWatchpointAtExpression =
new QAction(tr("Add Data Breakpoint at Expression \"%1\"").arg(name),
&breakpointMenu);
actSetWatchpointAtExpression->setToolTip(
tr("Setting a data breakpoint on an expression will cause the program "
"to stop when the data at the address given by the expression "
"is modified."));
breakpointMenu.addAction(actSetWatchpointAtVariableAddress);
if (actSetWatchpointAtPointerValue)
breakpointMenu.addAction(actSetWatchpointAtPointerValue);
breakpointMenu.addAction(actSetWatchpointAtExpression);
QMenu menu;
QAction *actInsertNewWatchItem = menu.addAction(tr("Insert New Evaluated Expression"));
actInsertNewWatchItem->setEnabled(canHandleWatches && canInsertWatches);
QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
actSelectWidgetToWatch->setEnabled(canHandleWatches && (engine->canWatchWidgets()));
menu.addSeparator();
QAction *actWatchExpression = new QAction(addWatchActionText(exp), &menu); QAction *actWatchExpression = new QAction(addWatchActionText(exp), &menu);
actWatchExpression->setEnabled(canHandleWatches && !exp.isEmpty()); actWatchExpression->setEnabled(canHandleWatches && !exp.isEmpty());
@@ -803,9 +820,7 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
menu.addAction(actSelectWidgetToWatch); menu.addAction(actSelectWidgetToWatch);
menu.addMenu(&formatMenu); menu.addMenu(&formatMenu);
menu.addMenu(&memoryMenu); menu.addMenu(&memoryMenu);
menu.addAction(actSetWatchpointAtVariableAddress); menu.addMenu(&breakpointMenu);
if (actSetWatchpointAtPointerValue)
menu.addAction(actSetWatchpointAtPointerValue);
menu.addAction(actCopy); menu.addAction(actCopy);
menu.addAction(actCopyValue); menu.addAction(actCopyValue);
menu.addSeparator(); menu.addSeparator();
@@ -868,9 +883,11 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
} else if (act == actOpenMemoryEditorStackLayout) { } else if (act == actOpenMemoryEditorStackLayout) {
addStackLayoutMemoryView(currentEngine(), false, mi0.model(), ev->globalPos(), this); addStackLayoutMemoryView(currentEngine(), false, mi0.model(), ev->globalPos(), this);
} else if (act == actSetWatchpointAtVariableAddress) { } else if (act == actSetWatchpointAtVariableAddress) {
setWatchpoint(address, size); setWatchpointAtAddress(address, size);
} else if (act == actSetWatchpointAtPointerValue) { } else if (act == actSetWatchpointAtPointerValue) {
setWatchpoint(pointerValue, 1); setWatchpointAtAddress(pointerValue, sizeof(void *)); // FIXME: an approximation..
} else if (act == actSetWatchpointAtExpression) {
setWatchpointAtExpression(name);
} else if (act == actSelectWidgetToWatch) { } else if (act == actSelectWidgetToWatch) {
grabMouse(Qt::CrossCursor); grabMouse(Qt::CrossCursor);
m_grabbing = true; m_grabbing = true;
@@ -1013,7 +1030,7 @@ void WatchWindow::setModelData
model()->setData(index, value, role); model()->setData(index, value, role);
} }
void WatchWindow::setWatchpoint(quint64 address, unsigned size) void WatchWindow::setWatchpointAtAddress(quint64 address, unsigned size)
{ {
BreakpointParameters data(WatchpointAtAddress); BreakpointParameters data(WatchpointAtAddress);
data.address = address; data.address = address;
@@ -1027,6 +1044,19 @@ void WatchWindow::setWatchpoint(quint64 address, unsigned size)
breakHandler()->appendBreakpoint(data); breakHandler()->appendBreakpoint(data);
} }
void WatchWindow::setWatchpointAtExpression(const QString &exp)
{
BreakpointParameters data(WatchpointAtExpression);
data.expression = exp;
BreakpointId id = breakHandler()->findWatchpoint(data);
if (id) {
qDebug() << "WATCHPOINT EXISTS";
// removeBreakpoint(index);
return;
}
breakHandler()->appendBreakpoint(data);
}
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -78,7 +78,8 @@ private:
void editItem(const QModelIndex &idx); void editItem(const QModelIndex &idx);
void resetHelper(const QModelIndex &idx); void resetHelper(const QModelIndex &idx);
void setWatchpoint(quint64 address, unsigned size); void setWatchpointAtAddress(quint64 address, unsigned size);
void setWatchpointAtExpression(const QString &exp);
void setModelData(int role, const QVariant &value = QVariant(), void setModelData(int role, const QVariant &value = QVariant(),
const QModelIndex &index = QModelIndex()); const QModelIndex &index = QModelIndex());