diff --git a/include/edb.h b/include/edb.h
index 3770dca36..38386309c 100644
--- a/include/edb.h
+++ b/include/edb.h
@@ -98,6 +98,7 @@ EDB_EXPORT void set_breakpoint_condition(address_t address, const QString &condi
EDB_EXPORT void toggle_breakpoint(address_t address);
EDB_EXPORT address_t current_data_view_address();
+EDB_EXPORT address_t instruction_pointer_address();
// change what the various views show
EDB_EXPORT bool dump_data_range(address_t address, address_t end_address, bool new_tab);
diff --git a/plugins/Bookmarks/BookmarkWidget.cpp b/plugins/Bookmarks/BookmarkWidget.cpp
index 47fc4b2a7..7b22ecf63 100644
--- a/plugins/Bookmarks/BookmarkWidget.cpp
+++ b/plugins/Bookmarks/BookmarkWidget.cpp
@@ -20,6 +20,7 @@ along with this program. If not, see .
#include "BookmarksModel.h"
#include "Expression.h"
#include "edb.h"
+#include "IBreakpoint.h"
#include
#include
#include
@@ -41,9 +42,13 @@ BookmarkWidget::BookmarkWidget(QWidget *parent, Qt::WindowFlags f)
ui.tableView->setModel(model_);
connect(edb::v1::debugger_ui, SIGNAL(detachEvent()), model_, SLOT(clearBookmarks()));
+ connect(edb::v1::disassembly_widget(), SIGNAL(signalUpdated()), model_, SLOT(updateList()));
connect(ui.buttonAdd, &QPushButton::clicked, this, &BookmarkWidget::buttonAddClicked);
connect(ui.buttonDel, &QPushButton::clicked, this, &BookmarkWidget::buttonDelClicked);
connect(ui.buttonClear, &QPushButton::clicked, this, &BookmarkWidget::buttonClearClicked);
+
+ toggleBreakpointAction_ = createAction(tr("&Toggle Breakpoint"), QKeySequence(tr("B")), &BookmarkWidget::toggleBreakpoint);
+ conditionalBreakpointAction_ = createAction(tr("Add &Conditional Breakpoint"), QKeySequence(tr("Shift+B")), &BookmarkWidget::addConditionalBreakpoint);
}
/**
@@ -123,6 +128,44 @@ void BookmarkWidget::buttonClearClicked() {
model_->clearBookmarks();
}
+/**
+ * @brief BookmarkWidget::toggleBreakpoint
+ */
+void BookmarkWidget::toggleBreakpoint() {
+
+ const QItemSelectionModel *const selModel = ui.tableView->selectionModel();
+ const QModelIndexList selections = selModel->selectedRows();
+
+ for (const auto index : selections) {
+ auto item = static_cast(index.internalPointer());
+ edb::v1::toggle_breakpoint(item->address);
+ }
+}
+
+/**
+ * @brief BookmarkWidget::addConditionalBreakpoint
+ */
+void BookmarkWidget::addConditionalBreakpoint() {
+
+ const QItemSelectionModel *const selModel = ui.tableView->selectionModel();
+ const QModelIndexList selections = selModel->selectedRows();
+
+ if (selections.size() == 1) {
+ bool ok;
+ const QString condition = QInputDialog::getText(this, tr("Set Breakpoint Condition"), tr("Expression:"), QLineEdit::Normal, QString(), &ok);
+ auto item = static_cast(selections[0].internalPointer());
+
+ if (ok) {
+ if (std::shared_ptr bp = edb::v1::create_breakpoint(item->address)) {
+ if (!condition.isEmpty()) {
+ bp->condition = condition;
+ }
+ }
+ }
+ }
+}
+
+
/**
* @brief BookmarkWidget::addAddress
* @param address
@@ -185,6 +228,8 @@ void BookmarkWidget::on_tableView_customContextMenuRequested(const QPoint &pos)
menu.addSeparator();
QAction *const actionComment = menu.addAction(tr("&Set Comment"));
QAction *const actionType = menu.addAction(tr("Set &Type"));
+ menu.addAction(toggleBreakpointAction_);
+ menu.addAction(conditionalBreakpointAction_);
QAction *const chosen = menu.exec(ui.tableView->mapToGlobal(pos));
if (chosen == actionAdd) {
@@ -230,6 +275,10 @@ void BookmarkWidget::on_tableView_customContextMenuRequested(const QPoint &pos)
}
}
}
+ } else if (chosen == toggleBreakpointAction_) {
+ toggleBreakpoint();
+ } else if (chosen == conditionalBreakpointAction_) {
+ addConditionalBreakpoint();
}
}
@@ -242,4 +291,16 @@ QList BookmarkWidget::entries() const {
return bookmarks.toList();
}
+// This is copied from Debugger::createAction, so really there should either be a class that implements
+// this that both inherit from, or this should be a generic function that takes the QObject* to act upon
+// as a parameter.
+template
+QAction *BookmarkWidget::createAction(const QString &text, const QKeySequence &keySequence, F func) {
+ auto action = new QAction(text, this);
+ action->setShortcut(keySequence);
+ addAction(action);
+ connect(action, &QAction::triggered, this, func);
+ return action;
+}
+
}
diff --git a/plugins/Bookmarks/BookmarkWidget.h b/plugins/Bookmarks/BookmarkWidget.h
index 4834a657c..dac8dd0b2 100644
--- a/plugins/Bookmarks/BookmarkWidget.h
+++ b/plugins/Bookmarks/BookmarkWidget.h
@@ -50,10 +50,18 @@ public Q_SLOTS:
void buttonAddClicked();
void buttonDelClicked();
void buttonClearClicked();
+ void toggleBreakpoint();
+ void addConditionalBreakpoint();
+
+private:
+ template
+ QAction *createAction(const QString &text, const QKeySequence &keySequence, F func);
private:
Ui::BookmarkWidget ui;
BookmarksModel *model_ = nullptr;
+ QAction *toggleBreakpointAction_;
+ QAction *conditionalBreakpointAction_;
};
}
diff --git a/plugins/Bookmarks/BookmarksModel.cpp b/plugins/Bookmarks/BookmarksModel.cpp
index 04c13ba4b..5aa7bc755 100644
--- a/plugins/Bookmarks/BookmarksModel.cpp
+++ b/plugins/Bookmarks/BookmarksModel.cpp
@@ -26,7 +26,10 @@ namespace BookmarksPlugin {
* @param parent
*/
BookmarksModel::BookmarksModel(QObject *parent)
- : QAbstractItemModel(parent) {
+ : QAbstractItemModel(parent),
+ breakpointIcon_(QLatin1String(":/debugger/images/breakpoint.svg")),
+ currentIcon_(QLatin1String(":/debugger/images/arrow-right.svg")),
+ currentBpIcon_(QLatin1String(":/debugger/images/arrow-right-red.svg")) {
}
/**
@@ -68,8 +71,17 @@ QVariant BookmarksModel::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) {
switch (index.column()) {
- case 0:
- return edb::v1::format_pointer(bookmark.address);
+ case 0: {
+ // Return the address with symbol name, if there is one
+ const QString symname = edb::v1::find_function_symbol(bookmark.address);
+ const QString address = edb::v1::format_pointer(bookmark.address);
+
+ if (!symname.isEmpty()) {
+ return tr("%1 <%2>").arg(address, symname);
+ }
+
+ return address;
+ }
case 1:
switch (bookmark.type) {
case Bookmark::Code:
@@ -87,6 +99,36 @@ QVariant BookmarksModel::data(const QModelIndex &index, int role) const {
}
}
+ if (role == Qt::DecorationRole) {
+ switch (index.column()) {
+ case 0: {
+ // Puts the correct icon (BP, current BP, current instruction) next to the address
+
+ // TODO: This is mostly copied from QDisassemblyView::drawSidebarElements, and both
+ // should really be factored out into a common location (although this uses icons
+ // and the other uses SVG renderers and painting).
+ const bool is_eip = (bookmark.address == edb::v1::instruction_pointer_address());
+ const bool has_breakpoint = (edb::v1::find_breakpoint(bookmark.address) != nullptr);
+
+ const QIcon *icon = nullptr;
+ if (is_eip) {
+ icon = has_breakpoint ? ¤tBpIcon_ : ¤tIcon_;
+ } else if (has_breakpoint) {
+ icon = &breakpointIcon_;
+ }
+
+ if (icon) {
+ return *icon;
+ }
+
+ // This acts as a dummy icon so even addresses with no icon will be aligned properly
+ return QColor(0, 0, 0, 0);
+ }
+ default:
+ return QVariant();
+ }
+ }
+
return QVariant();
}
@@ -200,6 +242,20 @@ void BookmarksModel::setType(const QModelIndex &index, const QString &type) {
Q_EMIT dataChanged(index, index);
}
+/**
+ * @brief BookmarksModel::updateList
+ */
+void BookmarksModel::updateList() {
+ // Every time the disassembly view changes, all the bookmark data is invalidated.
+ // This is not super expensive (unless you have a million bookmarks) but is not
+ // optimal either. Ideally we could factor out eip updates with the signalUpdated
+ // signal, and breakpoint updates with the toggleBreakPoint signal, but the latter
+ // can't use the SIGNAL/SLOT macros which means Debugger.h would need to be moved
+ // into the include directory.
+ beginResetModel();
+ endResetModel();
+}
+
/**
* @brief BookmarksModel::deleteBookmark
* @param index
diff --git a/plugins/Bookmarks/BookmarksModel.h b/plugins/Bookmarks/BookmarksModel.h
index a7eb27b50..89a537966 100644
--- a/plugins/Bookmarks/BookmarksModel.h
+++ b/plugins/Bookmarks/BookmarksModel.h
@@ -22,6 +22,7 @@ along with this program. If not, see .
#include "Types.h"
#include
#include
+#include
namespace BookmarksPlugin {
@@ -88,12 +89,16 @@ public Q_SLOTS:
void deleteBookmark(const QModelIndex &index);
void setComment(const QModelIndex &index, const QString &comment);
void setType(const QModelIndex &index, const QString &type);
+ void updateList();
public:
[[nodiscard]] const QVector &bookmarks() const { return bookmarks_; }
private:
QVector bookmarks_;
+ QIcon breakpointIcon_;
+ QIcon currentIcon_;
+ QIcon currentBpIcon_;
};
}
diff --git a/src/edb.cpp b/src/edb.cpp
index fe7b82890..fbb743e3e 100644
--- a/src/edb.cpp
+++ b/src/edb.cpp
@@ -1303,6 +1303,20 @@ address_t current_data_view_address() {
return qobject_cast(ui()->tabWidget_->currentWidget())->firstVisibleAddress();
}
+//------------------------------------------------------------------------------
+// Name: instruction_pointer_address
+// Desc: Returns the address pointed to by the instruction pointer.
+//------------------------------------------------------------------------------
+address_t instruction_pointer_address() {
+ if (IProcess *process = debugger_core->process()) {
+ State state;
+ process->currentThread()->getState(&state);
+ return state.instructionPointer();
+ }
+
+ return address_t{};
+}
+
//------------------------------------------------------------------------------
// Name: set_status
// Desc: