From 7b6cc2f16fa715505898a05d714ba2aafe23c9c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:12:36 +0000 Subject: [PATCH 01/12] Initial plan From 1c53ee1e7e97ec56ad938b35cdb3763a133aced2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:24:29 +0000 Subject: [PATCH 02/12] Add core enable/disable breakpoint functionality Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com> --- api/debuggerapi.h | 4 ++ api/debuggercontroller.cpp | 24 +++++++++ api/ffi.h | 10 ++++ core/debugadapter.h | 8 +++ core/debuggercontroller.cpp | 40 +++++++++++++++ core/debuggercontroller.h | 4 ++ core/debuggerstate.cpp | 98 +++++++++++++++++++++++++++++++++++++ core/debuggerstate.h | 12 +++++ core/ffi.cpp | 26 +++++++++- 9 files changed, 225 insertions(+), 1 deletion(-) diff --git a/api/debuggerapi.h b/api/debuggerapi.h index fc23d425..c334b513 100644 --- a/api/debuggerapi.h +++ b/api/debuggerapi.h @@ -719,6 +719,10 @@ namespace BinaryNinjaDebuggerAPI { void DeleteBreakpoint(const ModuleNameAndOffset& breakpoint); void AddBreakpoint(uint64_t address); void AddBreakpoint(const ModuleNameAndOffset& breakpoint); + void EnableBreakpoint(uint64_t address); + void EnableBreakpoint(const ModuleNameAndOffset& breakpoint); + void DisableBreakpoint(uint64_t address); + void DisableBreakpoint(const ModuleNameAndOffset& breakpoint); bool ContainsBreakpoint(uint64_t address); bool ContainsBreakpoint(const ModuleNameAndOffset& breakpoint); diff --git a/api/debuggercontroller.cpp b/api/debuggercontroller.cpp index 644565bf..d58985b1 100644 --- a/api/debuggercontroller.cpp +++ b/api/debuggercontroller.cpp @@ -760,6 +760,30 @@ void DebuggerController::AddBreakpoint(const ModuleNameAndOffset& breakpoint) } +void DebuggerController::EnableBreakpoint(uint64_t address) +{ + BNDebuggerEnableAbsoluteBreakpoint(m_object, address); +} + + +void DebuggerController::EnableBreakpoint(const ModuleNameAndOffset& breakpoint) +{ + BNDebuggerEnableRelativeBreakpoint(m_object, breakpoint.module.c_str(), breakpoint.offset); +} + + +void DebuggerController::DisableBreakpoint(uint64_t address) +{ + BNDebuggerDisableAbsoluteBreakpoint(m_object, address); +} + + +void DebuggerController::DisableBreakpoint(const ModuleNameAndOffset& breakpoint) +{ + BNDebuggerDisableRelativeBreakpoint(m_object, breakpoint.module.c_str(), breakpoint.offset); +} + + bool DebuggerController::ContainsBreakpoint(uint64_t address) { return BNDebuggerContainsAbsoluteBreakpoint(m_object, address); diff --git a/api/ffi.h b/api/ffi.h index 1f20dc86..a771bd48 100644 --- a/api/ffi.h +++ b/api/ffi.h @@ -252,6 +252,10 @@ extern "C" RelativeBreakpointAddedEvent, AbsoluteBreakpointRemovedEvent, RelativeBreakpointRemovedEvent, + AbsoluteBreakpointEnabledEvent, + RelativeBreakpointEnabledEvent, + AbsoluteBreakpointDisabledEvent, + RelativeBreakpointDisabledEvent, ActiveThreadChangedEvent, @@ -583,6 +587,12 @@ extern "C" DEBUGGER_FFI_API void BNDebuggerAddAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address); DEBUGGER_FFI_API void BNDebuggerAddRelativeBreakpoint( BNDebuggerController* controller, const char* module, uint64_t offset); + DEBUGGER_FFI_API void BNDebuggerEnableAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address); + DEBUGGER_FFI_API void BNDebuggerEnableRelativeBreakpoint( + BNDebuggerController* controller, const char* module, uint64_t offset); + DEBUGGER_FFI_API void BNDebuggerDisableAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address); + DEBUGGER_FFI_API void BNDebuggerDisableRelativeBreakpoint( + BNDebuggerController* controller, const char* module, uint64_t offset); DEBUGGER_FFI_API bool BNDebuggerContainsAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address); DEBUGGER_FFI_API bool BNDebuggerContainsRelativeBreakpoint( BNDebuggerController* controller, const char* module, uint64_t offset); diff --git a/core/debugadapter.h b/core/debugadapter.h index 04055c21..575fa368 100644 --- a/core/debugadapter.h +++ b/core/debugadapter.h @@ -264,6 +264,14 @@ namespace BinaryNinjaDebugger { virtual bool RemoveBreakpoint(const ModuleNameAndOffset& address) { return false; } + virtual bool EnableBreakpoint(const std::uintptr_t address) { return false; } + + virtual bool EnableBreakpoint(const ModuleNameAndOffset& address) { return false; } + + virtual bool DisableBreakpoint(const std::uintptr_t address) { return false; } + + virtual bool DisableBreakpoint(const ModuleNameAndOffset& address) { return false; } + virtual std::vector GetBreakpointList() const = 0; virtual std::unordered_map ReadAllRegisters() = 0; diff --git a/core/debuggercontroller.cpp b/core/debuggercontroller.cpp index 6e74b663..dc81f198 100644 --- a/core/debuggercontroller.cpp +++ b/core/debuggercontroller.cpp @@ -100,6 +100,46 @@ void DebuggerController::DeleteBreakpoint(const ModuleNameAndOffset& address) } +void DebuggerController::EnableBreakpoint(uint64_t address) +{ + m_state->EnableBreakpoint(address); + DebuggerEvent event; + event.type = AbsoluteBreakpointEnabledEvent; + event.data.absoluteAddress = address; + PostDebuggerEvent(event); +} + + +void DebuggerController::EnableBreakpoint(const ModuleNameAndOffset& address) +{ + m_state->EnableBreakpoint(address); + DebuggerEvent event; + event.type = RelativeBreakpointEnabledEvent; + event.data.relativeAddress = address; + PostDebuggerEvent(event); +} + + +void DebuggerController::DisableBreakpoint(uint64_t address) +{ + m_state->DisableBreakpoint(address); + DebuggerEvent event; + event.type = AbsoluteBreakpointDisabledEvent; + event.data.absoluteAddress = address; + PostDebuggerEvent(event); +} + + +void DebuggerController::DisableBreakpoint(const ModuleNameAndOffset& address) +{ + m_state->DisableBreakpoint(address); + DebuggerEvent event; + event.type = RelativeBreakpointDisabledEvent; + event.data.relativeAddress = address; + PostDebuggerEvent(event); +} + + bool DebuggerController::SetIP(uint64_t address) { std::string ipRegisterName; diff --git a/core/debuggercontroller.h b/core/debuggercontroller.h index d2b31867..67d78285 100644 --- a/core/debuggercontroller.h +++ b/core/debuggercontroller.h @@ -227,6 +227,10 @@ namespace BinaryNinjaDebugger { void AddBreakpoint(const ModuleNameAndOffset& address); void DeleteBreakpoint(uint64_t address); void DeleteBreakpoint(const ModuleNameAndOffset& address); + void EnableBreakpoint(uint64_t address); + void EnableBreakpoint(const ModuleNameAndOffset& address); + void DisableBreakpoint(uint64_t address); + void DisableBreakpoint(const ModuleNameAndOffset& address); DebugBreakpoint GetAllBreakpoints(); // registers diff --git a/core/debuggerstate.cpp b/core/debuggerstate.cpp index 0de03083..58f7af8e 100644 --- a/core/debuggerstate.cpp +++ b/core/debuggerstate.cpp @@ -534,6 +534,7 @@ bool DebuggerBreakpoints::AddAbsolute(uint64_t remoteAddress) { ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(remoteAddress); m_breakpoints.push_back(info); + m_enabledState[info] = true; // Enable by default SerializeMetadata(); } @@ -546,6 +547,7 @@ bool DebuggerBreakpoints::AddOffset(const ModuleNameAndOffset& address) if (!ContainsOffset(address)) { m_breakpoints.push_back(address); + m_enabledState[address] = true; // Enable by default SerializeMetadata(); // If the adapter is already created, we ask it to add the breakpoint. @@ -574,6 +576,7 @@ bool DebuggerBreakpoints::RemoveAbsolute(uint64_t remoteAddress) { m_breakpoints.erase(iter); } + m_enabledState.erase(info); // Remove enabled state SerializeMetadata(); m_state->GetAdapter()->RemoveBreakpoint(remoteAddress); return true; @@ -589,6 +592,7 @@ bool DebuggerBreakpoints::RemoveOffset(const ModuleNameAndOffset& address) if (auto iter = std::find(m_breakpoints.begin(), m_breakpoints.end(), address); iter != m_breakpoints.end()) m_breakpoints.erase(iter); + m_enabledState.erase(address); // Remove enabled state SerializeMetadata(); if (m_state->GetAdapter() && m_state->IsConnected()) @@ -603,6 +607,76 @@ bool DebuggerBreakpoints::RemoveOffset(const ModuleNameAndOffset& address) } +bool DebuggerBreakpoints::EnableAbsolute(uint64_t remoteAddress) +{ + ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(remoteAddress); + return EnableOffset(info); +} + + +bool DebuggerBreakpoints::EnableOffset(const ModuleNameAndOffset& address) +{ + if (!ContainsOffset(address)) + return false; + + m_enabledState[address] = true; + SerializeMetadata(); + + // If connected, make sure the breakpoint is active in the target + if (m_state->GetAdapter() && m_state->IsConnected()) + { + uint64_t remoteAddress = m_state->GetModules()->RelativeAddressToAbsolute(address); + m_state->GetAdapter()->AddBreakpoint(remoteAddress); + return true; + } + return true; +} + + +bool DebuggerBreakpoints::DisableAbsolute(uint64_t remoteAddress) +{ + ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(remoteAddress); + return DisableOffset(info); +} + + +bool DebuggerBreakpoints::DisableOffset(const ModuleNameAndOffset& address) +{ + if (!ContainsOffset(address)) + return false; + + m_enabledState[address] = false; + SerializeMetadata(); + + // If connected, remove the breakpoint from the target but keep it in our list + if (m_state->GetAdapter() && m_state->IsConnected()) + { + uint64_t remoteAddress = m_state->GetModules()->RelativeAddressToAbsolute(address); + m_state->GetAdapter()->RemoveBreakpoint(remoteAddress); + return true; + } + return true; +} + + +bool DebuggerBreakpoints::IsEnabledAbsolute(uint64_t address) +{ + ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(address); + return IsEnabledOffset(info); +} + + +bool DebuggerBreakpoints::IsEnabledOffset(const ModuleNameAndOffset& address) +{ + auto iter = m_enabledState.find(address); + if (iter != m_enabledState.end()) + return iter->second; + + // Default to enabled if not explicitly set + return true; +} + + bool DebuggerBreakpoints::ContainsOffset(const ModuleNameAndOffset& address) { // If there is no backend, then only check if the breakpoint is in the list @@ -980,6 +1054,30 @@ void DebuggerState::DeleteBreakpoint(const ModuleNameAndOffset& address) } +void DebuggerState::EnableBreakpoint(uint64_t address) +{ + m_breakpoints->EnableAbsolute(address); +} + + +void DebuggerState::EnableBreakpoint(const ModuleNameAndOffset& address) +{ + m_breakpoints->EnableOffset(address); +} + + +void DebuggerState::DisableBreakpoint(uint64_t address) +{ + m_breakpoints->DisableAbsolute(address); +} + + +void DebuggerState::DisableBreakpoint(const ModuleNameAndOffset& address) +{ + m_breakpoints->DisableOffset(address); +} + + uint64_t DebuggerState::IP() { if (!IsConnected()) diff --git a/core/debuggerstate.h b/core/debuggerstate.h index 82719cd1..5a0b7a08 100644 --- a/core/debuggerstate.h +++ b/core/debuggerstate.h @@ -16,6 +16,7 @@ limitations under the License. #pragma once +#include #include "binaryninjaapi.h" #include "ui/uitypes.h" #include "debugadaptertype.h" @@ -82,6 +83,7 @@ namespace BinaryNinjaDebugger { private: DebuggerState* m_state; std::vector m_breakpoints; + std::unordered_map m_enabledState; public: DebuggerBreakpoints(DebuggerState* state, std::vector initial = {}); @@ -89,8 +91,14 @@ namespace BinaryNinjaDebugger { bool AddOffset(const ModuleNameAndOffset& address); bool RemoveAbsolute(uint64_t remoteAddress); bool RemoveOffset(const ModuleNameAndOffset& address); + bool EnableAbsolute(uint64_t remoteAddress); + bool EnableOffset(const ModuleNameAndOffset& address); + bool DisableAbsolute(uint64_t remoteAddress); + bool DisableOffset(const ModuleNameAndOffset& address); bool ContainsAbsolute(uint64_t address); bool ContainsOffset(const ModuleNameAndOffset& address); + bool IsEnabledAbsolute(uint64_t address); + bool IsEnabledOffset(const ModuleNameAndOffset& address); void Apply(); void SerializeMetadata(); void UnserializedMetadata(); @@ -237,6 +245,10 @@ namespace BinaryNinjaDebugger { void AddBreakpoint(const ModuleNameAndOffset& address); void DeleteBreakpoint(uint64_t address); void DeleteBreakpoint(const ModuleNameAndOffset& address); + void EnableBreakpoint(uint64_t address); + void EnableBreakpoint(const ModuleNameAndOffset& address); + void DisableBreakpoint(uint64_t address); + void DisableBreakpoint(const ModuleNameAndOffset& address); uint64_t IP(); uint64_t StackPointer(); diff --git a/core/ffi.cpp b/core/ffi.cpp index 0b59285a..583edc6c 100644 --- a/core/ffi.cpp +++ b/core/ffi.cpp @@ -813,7 +813,7 @@ BNDebugBreakpoint* BNDebuggerGetBreakpoints(BNDebuggerController* controller, si for (size_t i = 0; i < breakpoints.size(); i++) { uint64_t remoteAddress = state->GetModules()->RelativeAddressToAbsolute(breakpoints[i]); - bool enabled = false; + bool enabled = state->GetBreakpoints()->IsEnabledOffset(breakpoints[i]); result[i].module = BNDebuggerAllocString(breakpoints[i].module.c_str()); result[i].offset = breakpoints[i].offset; result[i].address = remoteAddress; @@ -857,6 +857,30 @@ void BNDebuggerAddRelativeBreakpoint(BNDebuggerController* controller, const cha } +void BNDebuggerEnableAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address) +{ + controller->object->EnableBreakpoint(address); +} + + +void BNDebuggerEnableRelativeBreakpoint(BNDebuggerController* controller, const char* module, uint64_t offset) +{ + controller->object->EnableBreakpoint(ModuleNameAndOffset(module, offset)); +} + + +void BNDebuggerDisableAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address) +{ + controller->object->DisableBreakpoint(address); +} + + +void BNDebuggerDisableRelativeBreakpoint(BNDebuggerController* controller, const char* module, uint64_t offset) +{ + controller->object->DisableBreakpoint(ModuleNameAndOffset(module, offset)); +} + + uint64_t BNDebuggerGetIP(BNDebuggerController* controller) { return controller->object->GetCurrentIP(); From 4e11fdbec2c1b6a0152983a3a22dc723635654b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:27:28 +0000 Subject: [PATCH 03/12] Add UI support for enabling/disabling breakpoints Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com> --- ui/breakpointswidget.cpp | 70 +++++++++++++++++++++++++++++++++++----- ui/breakpointswidget.h | 7 ++-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/ui/breakpointswidget.cpp b/ui/breakpointswidget.cpp index 666b0c2a..ace3d231 100644 --- a/ui/breakpointswidget.cpp +++ b/ui/breakpointswidget.cpp @@ -104,11 +104,11 @@ QVariant DebugBreakpointsListModel::data(const QModelIndex& index, int role) con switch (index.column()) { -// case DebugBreakpointsListModel::EnabledColumn: -// { -// QString text = item->enabled() ? "true" : "false"; -// return QVariant(text); -// } + case DebugBreakpointsListModel::EnabledColumn: + { + QString text = item->enabled() ? "☑" : "☐"; + return QVariant(text); + } case DebugBreakpointsListModel::LocationColumn: { QString text; @@ -152,8 +152,8 @@ QVariant DebugBreakpointsListModel::headerData(int column, Qt::Orientation orien switch (column) { -// case DebugBreakpointsListModel::EnabledColumn: -// return "Enabled"; + case DebugBreakpointsListModel::EnabledColumn: + return "Enabled"; case DebugBreakpointsListModel::LocationColumn: return "Location"; case DebugBreakpointsListModel::AddressColumn: @@ -197,7 +197,7 @@ void DebugBreakpointsItemDelegate::paint( auto data = idx.data(Qt::DisplayRole); switch (idx.column()) { -// case DebugBreakpointsListModel::EnabledColumn: + case DebugBreakpointsListModel::EnabledColumn: case DebugBreakpointsListModel::LocationColumn: case DebugBreakpointsListModel::AddressColumn: { @@ -291,6 +291,24 @@ DebugBreakpointsWidget::DebugBreakpointsWidget(ViewFrame* view, BinaryViewRef da m_actionHandler.bindAction( addBreakpointActionName, UIAction([&]() { add(); })); + QString enableBreakpointActionName = QString::fromStdString("Enable Breakpoint"); + UIAction::registerAction(enableBreakpointActionName); + m_menu->addAction(enableBreakpointActionName, "Options", MENU_ORDER_NORMAL); + m_actionHandler.bindAction( + enableBreakpointActionName, UIAction([&]() { enableSelected(); }, [&]() { return selectionNotEmpty(); })); + + QString disableBreakpointActionName = QString::fromStdString("Disable Breakpoint"); + UIAction::registerAction(disableBreakpointActionName); + m_menu->addAction(disableBreakpointActionName, "Options", MENU_ORDER_NORMAL); + m_actionHandler.bindAction( + disableBreakpointActionName, UIAction([&]() { disableSelected(); }, [&]() { return selectionNotEmpty(); })); + + QString toggleBreakpointActionName = QString::fromStdString("Toggle Breakpoint Enable/Disable"); + UIAction::registerAction(toggleBreakpointActionName, QKeySequence("Ctrl+Shift+B")); + m_menu->addAction(toggleBreakpointActionName, "Options", MENU_ORDER_NORMAL); + m_actionHandler.bindAction( + toggleBreakpointActionName, UIAction([&]() { toggleSelected(); }, [&]() { return selectionNotEmpty(); })); + connect(this, &QTableView::doubleClicked, this, &DebugBreakpointsWidget::onDoubleClicked); updateContent(); @@ -418,6 +436,42 @@ void DebugBreakpointsWidget::add() } +void DebugBreakpointsWidget::enableSelected() +{ + QModelIndexList sel = selectionModel()->selectedRows(); + for (const QModelIndex& index : sel) + { + BreakpointItem bp = m_model->getRow(index.row()); + m_controller->EnableBreakpoint(bp.location()); + } +} + + +void DebugBreakpointsWidget::disableSelected() +{ + QModelIndexList sel = selectionModel()->selectedRows(); + for (const QModelIndex& index : sel) + { + BreakpointItem bp = m_model->getRow(index.row()); + m_controller->DisableBreakpoint(bp.location()); + } +} + + +void DebugBreakpointsWidget::toggleSelected() +{ + QModelIndexList sel = selectionModel()->selectedRows(); + for (const QModelIndex& index : sel) + { + BreakpointItem bp = m_model->getRow(index.row()); + if (bp.enabled()) + m_controller->DisableBreakpoint(bp.location()); + else + m_controller->EnableBreakpoint(bp.location()); + } +} + + void DebugBreakpointsWidget::remove() { QModelIndexList sel = selectionModel()->selectedRows(); diff --git a/ui/breakpointswidget.h b/ui/breakpointswidget.h index 3a5a3f4f..8241974e 100644 --- a/ui/breakpointswidget.h +++ b/ui/breakpointswidget.h @@ -65,7 +65,7 @@ class DebugBreakpointsListModel : public QAbstractTableModel public: enum ColumnHeaders { - //EnabledColumn, + EnabledColumn, LocationColumn, AddressColumn, }; @@ -83,7 +83,7 @@ class DebugBreakpointsListModel : public QAbstractTableModel virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override { (void)parent; - return 2; + return 3; } BreakpointItem getRow(int row) const; virtual QVariant data(const QModelIndex& i, int role) const override; @@ -148,6 +148,9 @@ private slots: void remove(); void onDoubleClicked(); void add(); + void enableSelected(); + void disableSelected(); + void toggleSelected(); public slots: void updateContent(); From 17f734be64433bcb4f3a257113ce4a4d21a9e6db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:29:39 +0000 Subject: [PATCH 04/12] Add mouse click support for toggling breakpoint enable state Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com> --- ui/breakpointswidget.cpp | 20 ++++++++++++++++++++ ui/breakpointswidget.h | 1 + 2 files changed, 21 insertions(+) diff --git a/ui/breakpointswidget.cpp b/ui/breakpointswidget.cpp index ace3d231..2410bb52 100644 --- a/ui/breakpointswidget.cpp +++ b/ui/breakpointswidget.cpp @@ -22,6 +22,7 @@ limitations under the License. #include #include #include +#include #include "breakpointswidget.h" #include "ui.h" #include "menus.h" @@ -349,6 +350,25 @@ void DebugBreakpointsWidget::keyPressEvent(QKeyEvent* event) } +void DebugBreakpointsWidget::mousePressEvent(QMouseEvent* event) +{ + QModelIndex index = indexAt(event->pos()); + if (index.isValid() && index.column() == DebugBreakpointsListModel::EnabledColumn) + { + // Toggle breakpoint enabled state when clicking on enabled column + BreakpointItem bp = m_model->getRow(index.row()); + if (bp.enabled()) + m_controller->DisableBreakpoint(bp.location()); + else + m_controller->EnableBreakpoint(bp.location()); + return; // Don't call parent to avoid selection change + } + + // Call parent for normal behavior + QTableView::mousePressEvent(event); +} + + bool DebugBreakpointsWidget::selectionNotEmpty() { QModelIndexList sel = selectionModel()->selectedIndexes(); diff --git a/ui/breakpointswidget.h b/ui/breakpointswidget.h index 8241974e..40f6619e 100644 --- a/ui/breakpointswidget.h +++ b/ui/breakpointswidget.h @@ -135,6 +135,7 @@ class DebugBreakpointsWidget : public QTableView virtual void contextMenuEvent(QContextMenuEvent* event) override; virtual void keyPressEvent(QKeyEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; public: DebugBreakpointsWidget(ViewFrame* view, BinaryViewRef data, Menu* menu); From c8020d3016cba29fb2496566bf7e11c1ccd50d06 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Tue, 21 Oct 2025 13:54:36 +0800 Subject: [PATCH 05/12] Fix compiling --- core/debuggerstate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/debuggerstate.h b/core/debuggerstate.h index 5a0b7a08..065edf1d 100644 --- a/core/debuggerstate.h +++ b/core/debuggerstate.h @@ -83,7 +83,7 @@ namespace BinaryNinjaDebugger { private: DebuggerState* m_state; std::vector m_breakpoints; - std::unordered_map m_enabledState; + std::map m_enabledState; public: DebuggerBreakpoints(DebuggerState* state, std::vector initial = {}); From 0c6ab835210d29ef7999422f1d01e73851bb323d Mon Sep 17 00:00:00 2001 From: Xusheng Date: Tue, 21 Oct 2025 14:31:09 +0800 Subject: [PATCH 06/12] Various fixes --- ui/breakpointswidget.cpp | 6 +++++- ui/debuggerwidget.cpp | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/breakpointswidget.cpp b/ui/breakpointswidget.cpp index 2410bb52..5ea5d21a 100644 --- a/ui/breakpointswidget.cpp +++ b/ui/breakpointswidget.cpp @@ -154,7 +154,7 @@ QVariant DebugBreakpointsListModel::headerData(int column, Qt::Orientation orien switch (column) { case DebugBreakpointsListModel::EnabledColumn: - return "Enabled"; + return ""; case DebugBreakpointsListModel::LocationColumn: return "Location"; case DebugBreakpointsListModel::AddressColumn: @@ -524,4 +524,8 @@ void DebugBreakpointsWidget::updateContent() } m_model->updateRows(bps); + + resizeColumnToContents(DebugBreakpointsListModel::EnabledColumn); + resizeColumnToContents(DebugBreakpointsListModel::LocationColumn); + resizeColumnToContents(DebugBreakpointsListModel::AddressColumn); } diff --git a/ui/debuggerwidget.cpp b/ui/debuggerwidget.cpp index 2629cd15..9824c6e7 100644 --- a/ui/debuggerwidget.cpp +++ b/ui/debuggerwidget.cpp @@ -92,6 +92,10 @@ void DebuggerWidget::uiEventHandler(const DebuggerEvent& event) case AbsoluteBreakpointAddedEvent: case RelativeBreakpointRemovedEvent: case AbsoluteBreakpointRemovedEvent: + case AbsoluteBreakpointEnabledEvent: + case RelativeBreakpointEnabledEvent: + case AbsoluteBreakpointDisabledEvent: + case RelativeBreakpointDisabledEvent: m_breakpointsWidget->updateContent(); break; default: From 51a0800ed417b26099850951db3ffe69ca50638c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 06:45:19 +0000 Subject: [PATCH 07/12] Implement breakpoint enable/disable improvements - Consolidate context menu: replace Enable/Disable/Toggle with single Toggle action - Add Enable All, Disable All, and Solo Breakpoint context menu actions - Update render layer to only show enabled breakpoints - Add context menu actions in debugger for enable/disable with dynamic visibility - Make Python API DebugBreakpoint.enabled field read-only - Add enable_breakpoint() and disable_breakpoint() to Python API Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com> --- api/python/debuggercontroller.py | 39 ++++++++++++-- ui/breakpointswidget.cpp | 86 +++++++++++++++++++++---------- ui/breakpointswidget.h | 5 +- ui/renderlayer.cpp | 31 ++++++++++- ui/ui.cpp | 88 ++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 33 deletions(-) diff --git a/api/python/debuggercontroller.py b/api/python/debuggercontroller.py index 7058144b..7c92c216 100644 --- a/api/python/debuggercontroller.py +++ b/api/python/debuggercontroller.py @@ -260,7 +260,7 @@ class DebugBreakpoint: * ``module``: the name of the module for which the breakpoint is in * ``offset``: the offset of the breakpoint to the start of the module * ``address``: the absolute address of the breakpoint - * ``enabled``: not used + * ``enabled``: whether the breakpoint is enabled (read-only) """ def __init__(self, module, offset, address, enabled): @@ -281,7 +281,7 @@ def __ne__(self, other): return not (self == other) def __hash__(self): - return hash((self.module, self.offset, self.address. self.enabled)) + return hash((self.module, self.offset, self.address, self.enabled)) def __setattr__(self, name, value): try: @@ -290,7 +290,8 @@ def __setattr__(self, name, value): raise AttributeError(f"attribute '{name}' is read only") def __repr__(self): - return f"" + status = "enabled" if self.enabled else "disabled" + return f"" class ModuleNameAndOffset: @@ -1958,6 +1959,38 @@ def has_breakpoint(self, address) -> bool: else: raise NotImplementedError + def enable_breakpoint(self, address): + """ + Enable a breakpoint + + The input can be either an absolute address, or a ModuleNameAndOffset, which specifies a relative address to the + start of a module. The latter is useful for ASLR. + + :param address: the address of breakpoint to enable + """ + if isinstance(address, int): + dbgcore.BNDebuggerEnableAbsoluteBreakpoint(self.handle, address) + elif isinstance(address, ModuleNameAndOffset): + dbgcore.BNDebuggerEnableRelativeBreakpoint(self.handle, address.module, address.offset) + else: + raise NotImplementedError + + def disable_breakpoint(self, address): + """ + Disable a breakpoint + + The input can be either an absolute address, or a ModuleNameAndOffset, which specifies a relative address to the + start of a module. The latter is useful for ASLR. + + :param address: the address of breakpoint to disable + """ + if isinstance(address, int): + dbgcore.BNDebuggerDisableAbsoluteBreakpoint(self.handle, address) + elif isinstance(address, ModuleNameAndOffset): + dbgcore.BNDebuggerDisableRelativeBreakpoint(self.handle, address.module, address.offset) + else: + raise NotImplementedError + @property def ip(self) -> int: """ diff --git a/ui/breakpointswidget.cpp b/ui/breakpointswidget.cpp index 5ea5d21a..ee9e2c9c 100644 --- a/ui/breakpointswidget.cpp +++ b/ui/breakpointswidget.cpp @@ -292,23 +292,29 @@ DebugBreakpointsWidget::DebugBreakpointsWidget(ViewFrame* view, BinaryViewRef da m_actionHandler.bindAction( addBreakpointActionName, UIAction([&]() { add(); })); - QString enableBreakpointActionName = QString::fromStdString("Enable Breakpoint"); - UIAction::registerAction(enableBreakpointActionName); - m_menu->addAction(enableBreakpointActionName, "Options", MENU_ORDER_NORMAL); + QString toggleBreakpointActionName = QString::fromStdString("Toggle Breakpoint"); + UIAction::registerAction(toggleBreakpointActionName, QKeySequence("Ctrl+Shift+B")); + m_menu->addAction(toggleBreakpointActionName, "Options", MENU_ORDER_NORMAL); m_actionHandler.bindAction( - enableBreakpointActionName, UIAction([&]() { enableSelected(); }, [&]() { return selectionNotEmpty(); })); + toggleBreakpointActionName, UIAction([&]() { toggleSelected(); }, [&]() { return selectionNotEmpty(); })); - QString disableBreakpointActionName = QString::fromStdString("Disable Breakpoint"); - UIAction::registerAction(disableBreakpointActionName); - m_menu->addAction(disableBreakpointActionName, "Options", MENU_ORDER_NORMAL); + QString enableAllActionName = QString::fromStdString("Enable All Breakpoints"); + UIAction::registerAction(enableAllActionName); + m_menu->addAction(enableAllActionName, "Options", MENU_ORDER_NORMAL); m_actionHandler.bindAction( - disableBreakpointActionName, UIAction([&]() { disableSelected(); }, [&]() { return selectionNotEmpty(); })); + enableAllActionName, UIAction([&]() { enableAll(); })); - QString toggleBreakpointActionName = QString::fromStdString("Toggle Breakpoint Enable/Disable"); - UIAction::registerAction(toggleBreakpointActionName, QKeySequence("Ctrl+Shift+B")); - m_menu->addAction(toggleBreakpointActionName, "Options", MENU_ORDER_NORMAL); + QString disableAllActionName = QString::fromStdString("Disable All Breakpoints"); + UIAction::registerAction(disableAllActionName); + m_menu->addAction(disableAllActionName, "Options", MENU_ORDER_NORMAL); m_actionHandler.bindAction( - toggleBreakpointActionName, UIAction([&]() { toggleSelected(); }, [&]() { return selectionNotEmpty(); })); + disableAllActionName, UIAction([&]() { disableAll(); })); + + QString soloBreakpointActionName = QString::fromStdString("Solo Breakpoint"); + UIAction::registerAction(soloBreakpointActionName); + m_menu->addAction(soloBreakpointActionName, "Options", MENU_ORDER_NORMAL); + m_actionHandler.bindAction( + soloBreakpointActionName, UIAction([&]() { soloSelected(); }, [&]() { return selectionNotEmpty(); })); connect(this, &QTableView::doubleClicked, this, &DebugBreakpointsWidget::onDoubleClicked); @@ -456,39 +462,67 @@ void DebugBreakpointsWidget::add() } -void DebugBreakpointsWidget::enableSelected() +void DebugBreakpointsWidget::toggleSelected() { QModelIndexList sel = selectionModel()->selectedRows(); for (const QModelIndex& index : sel) { BreakpointItem bp = m_model->getRow(index.row()); - m_controller->EnableBreakpoint(bp.location()); + if (bp.enabled()) + m_controller->DisableBreakpoint(bp.location()); + else + m_controller->EnableBreakpoint(bp.location()); } } -void DebugBreakpointsWidget::disableSelected() +void DebugBreakpointsWidget::enableAll() { - QModelIndexList sel = selectionModel()->selectedRows(); - for (const QModelIndex& index : sel) + std::vector breakpoints = m_controller->GetBreakpoints(); + for (const DebugBreakpoint& bp : breakpoints) { - BreakpointItem bp = m_model->getRow(index.row()); - m_controller->DisableBreakpoint(bp.location()); + ModuleNameAndOffset info; + info.module = bp.module; + info.offset = bp.offset; + m_controller->EnableBreakpoint(info); } } -void DebugBreakpointsWidget::toggleSelected() +void DebugBreakpointsWidget::disableAll() +{ + std::vector breakpoints = m_controller->GetBreakpoints(); + for (const DebugBreakpoint& bp : breakpoints) + { + ModuleNameAndOffset info; + info.module = bp.module; + info.offset = bp.offset; + m_controller->DisableBreakpoint(info); + } +} + + +void DebugBreakpointsWidget::soloSelected() { QModelIndexList sel = selectionModel()->selectedRows(); - for (const QModelIndex& index : sel) + if (sel.empty()) + return; + + // Get the selected breakpoint location + BreakpointItem selectedBp = m_model->getRow(sel[0].row()); + + // Disable all breakpoints first + std::vector breakpoints = m_controller->GetBreakpoints(); + for (const DebugBreakpoint& bp : breakpoints) { - BreakpointItem bp = m_model->getRow(index.row()); - if (bp.enabled()) - m_controller->DisableBreakpoint(bp.location()); - else - m_controller->EnableBreakpoint(bp.location()); + ModuleNameAndOffset info; + info.module = bp.module; + info.offset = bp.offset; + m_controller->DisableBreakpoint(info); } + + // Enable the selected breakpoint + m_controller->EnableBreakpoint(selectedBp.location()); } diff --git a/ui/breakpointswidget.h b/ui/breakpointswidget.h index 40f6619e..2df230a8 100644 --- a/ui/breakpointswidget.h +++ b/ui/breakpointswidget.h @@ -149,9 +149,10 @@ private slots: void remove(); void onDoubleClicked(); void add(); - void enableSelected(); - void disableSelected(); void toggleSelected(); + void enableAll(); + void disableAll(); + void soloSelected(); public slots: void updateContent(); diff --git a/ui/renderlayer.cpp b/ui/renderlayer.cpp index 7d91185f..0880d4dc 100644 --- a/ui/renderlayer.cpp +++ b/ui/renderlayer.cpp @@ -17,6 +17,7 @@ limitations under the License. #include "renderlayer.h" #include "ttdcoveragerenderlayer.h" #include "debuggerapi.h" +#include using namespace BinaryNinja; using namespace BinaryNinjaDebuggerAPI; @@ -37,6 +38,14 @@ void DebuggerRenderLayer::ApplyToBlock(Ref block, std::vectorIP(); bool paused = controller->GetTargetStatus() == DebugAdapterPausedStatus; + // Get all breakpoints with their enabled state + std::vector breakpoints = controller->GetBreakpoints(); + std::map breakpointEnabledMap; + for (const auto& bp : breakpoints) + { + breakpointEnabledMap[bp.address] = bp.enabled; + } + for (auto& line : lines) { // Do not draw the tags on an empty line, e.g., those separating the basic blocks in the linear view @@ -44,7 +53,12 @@ void DebuggerRenderLayer::ApplyToBlock(Ref block, std::vectorContainsBreakpoint(line.addr); + // Only render enabled breakpoints + bool hasBreakpoint = false; + if (breakpointEnabledMap.count(line.addr) > 0) + { + hasBreakpoint = breakpointEnabledMap[line.addr]; + } if (hasPC && hasBreakpoint) { @@ -141,11 +155,24 @@ void DebuggerRenderLayer::ApplyToHighLevelILBody(Ref function, std::ve uint64_t ipAddr = controller->IP(); bool paused = controller->GetTargetStatus() == DebugAdapterPausedStatus; + // Get all breakpoints with their enabled state + std::vector breakpoints = controller->GetBreakpoints(); + std::map breakpointEnabledMap; + for (const auto& bp : breakpoints) + { + breakpointEnabledMap[bp.address] = bp.enabled; + } + for (auto& linearLine : lines) { DisassemblyTextLine& line = linearLine.contents; bool hasPC = (line.addr == ipAddr) && paused; - bool hasBreakpoint = controller->ContainsBreakpoint(line.addr); + // Only render enabled breakpoints + bool hasBreakpoint = false; + if (breakpointEnabledMap.count(line.addr) > 0) + { + hasBreakpoint = breakpointEnabledMap[line.addr]; + } if (hasPC && hasBreakpoint) { diff --git a/ui/ui.cpp b/ui/ui.cpp index 16f284ec..933e3cec 100644 --- a/ui/ui.cpp +++ b/ui/ui.cpp @@ -811,6 +811,94 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) requireBinaryView)); debuggerMenu->addAction("Toggle Breakpoint", "Breakpoint"); + // Helper function to check if there's an enabled breakpoint at the current address + auto hasEnabledBreakpoint = [](BinaryView* view, uint64_t addr) -> bool { + auto controller = DebuggerController::GetController(view); + if (!controller) + return false; + + std::vector breakpoints = controller->GetBreakpoints(); + for (const auto& bp : breakpoints) + { + if (bp.address == addr) + return bp.enabled; + } + return false; + }; + + // Helper function to check if there's a disabled breakpoint at the current address + auto hasDisabledBreakpoint = [](BinaryView* view, uint64_t addr) -> bool { + auto controller = DebuggerController::GetController(view); + if (!controller) + return false; + + std::vector breakpoints = controller->GetBreakpoints(); + for (const auto& bp : breakpoints) + { + if (bp.address == addr) + return !bp.enabled; + } + return false; + }; + + // Register "Enable Breakpoint" action (shown when breakpoint is disabled) + UIAction::registerAction("Enable Breakpoint"); + context->globalActions()->bindAction("Enable Breakpoint", + UIAction( + [=](const UIActionContext& ctxt) { + if (!ctxt.binaryView) + return; + auto controller = DebuggerController::GetController(ctxt.binaryView); + if (!controller) + return; + + bool isAbsoluteAddress = controller->IsConnected(); + if (isAbsoluteAddress) + { + controller->EnableBreakpoint(ctxt.address); + } + else + { + std::string filename = controller->GetInputFile(); + uint64_t offset = ctxt.address - controller->GetViewFileSegmentsStart(); + ModuleNameAndOffset info = {filename, offset}; + controller->EnableBreakpoint(info); + } + }, + [=](const UIActionContext& ctxt) { + return ctxt.binaryView && hasDisabledBreakpoint(ctxt.binaryView, ctxt.address); + })); + debuggerMenu->addAction("Enable Breakpoint", "Breakpoint"); + + // Register "Disable Breakpoint" action (shown when breakpoint is enabled) + UIAction::registerAction("Disable Breakpoint"); + context->globalActions()->bindAction("Disable Breakpoint", + UIAction( + [=](const UIActionContext& ctxt) { + if (!ctxt.binaryView) + return; + auto controller = DebuggerController::GetController(ctxt.binaryView); + if (!controller) + return; + + bool isAbsoluteAddress = controller->IsConnected(); + if (isAbsoluteAddress) + { + controller->DisableBreakpoint(ctxt.address); + } + else + { + std::string filename = controller->GetInputFile(); + uint64_t offset = ctxt.address - controller->GetViewFileSegmentsStart(); + ModuleNameAndOffset info = {filename, offset}; + controller->DisableBreakpoint(info); + } + }, + [=](const UIActionContext& ctxt) { + return ctxt.binaryView && hasEnabledBreakpoint(ctxt.binaryView, ctxt.address); + })); + debuggerMenu->addAction("Disable Breakpoint", "Breakpoint"); + UIAction::registerAction("Connect to Debug Server"); context->globalActions()->bindAction("Connect to Debug Server", UIAction( From 66c75e5a579df245966694fa19dc7f484fa2cb5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 06:55:09 +0000 Subject: [PATCH 08/12] Rename "Toggle Breakpoint" to "Toggle Enabled" in breakpoints widget Avoid naming conflict with the existing F2 "Toggle Breakpoint" action that adds/removes breakpoints. The breakpoints widget action (Ctrl+Shift+B) now called "Toggle Enabled" to clearly indicate it toggles the enabled state of existing breakpoints. Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com> --- ui/breakpointswidget.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/breakpointswidget.cpp b/ui/breakpointswidget.cpp index ee9e2c9c..83c6b8d8 100644 --- a/ui/breakpointswidget.cpp +++ b/ui/breakpointswidget.cpp @@ -292,11 +292,11 @@ DebugBreakpointsWidget::DebugBreakpointsWidget(ViewFrame* view, BinaryViewRef da m_actionHandler.bindAction( addBreakpointActionName, UIAction([&]() { add(); })); - QString toggleBreakpointActionName = QString::fromStdString("Toggle Breakpoint"); - UIAction::registerAction(toggleBreakpointActionName, QKeySequence("Ctrl+Shift+B")); - m_menu->addAction(toggleBreakpointActionName, "Options", MENU_ORDER_NORMAL); + QString toggleEnabledActionName = QString::fromStdString("Toggle Enabled"); + UIAction::registerAction(toggleEnabledActionName, QKeySequence("Ctrl+Shift+B")); + m_menu->addAction(toggleEnabledActionName, "Options", MENU_ORDER_NORMAL); m_actionHandler.bindAction( - toggleBreakpointActionName, UIAction([&]() { toggleSelected(); }, [&]() { return selectionNotEmpty(); })); + toggleEnabledActionName, UIAction([&]() { toggleSelected(); }, [&]() { return selectionNotEmpty(); })); QString enableAllActionName = QString::fromStdString("Enable All Breakpoints"); UIAction::registerAction(enableAllActionName); From bc7dc52e483b0755b5ae65502f097af426580ad2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 07:14:24 +0000 Subject: [PATCH 09/12] Refine enable/disable breakpoint actions and disabled breakpoint rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Merge Enable/Disable actions into dynamic single action that changes name based on breakpoint state 2. Add Solo Breakpoint action to context menu 3. Change disabled breakpoint rendering to show "🛑︎" tag without line highlighting 4. Handle case where IP and disabled breakpoint are on same line (shows "🛑︎➞" with IP highlighting) Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com> --- ui/renderlayer.cpp | 124 +++++++++++++++++++++++++++++++++++++++++---- ui/ui.cpp | 113 +++++++++++++++++++++++++++++------------ 2 files changed, 196 insertions(+), 41 deletions(-) diff --git a/ui/renderlayer.cpp b/ui/renderlayer.cpp index 0880d4dc..68939c97 100644 --- a/ui/renderlayer.cpp +++ b/ui/renderlayer.cpp @@ -53,14 +53,18 @@ void DebuggerRenderLayer::ApplyToBlock(Ref block, std::vector 0) { - hasBreakpoint = breakpointEnabledMap[line.addr]; + if (breakpointEnabledMap[line.addr]) + hasEnabledBreakpoint = true; + else + hasDisabledBreakpoint = true; } - if (hasPC && hasBreakpoint) + if (hasPC && hasEnabledBreakpoint) { bool appliedTag = false; for (size_t i = 0; i < line.tokens.size(); i++) @@ -87,6 +91,34 @@ void DebuggerRenderLayer::ApplyToBlock(Ref block, std::vector block, std::vector block, std::vector function, std::ve { DisassemblyTextLine& line = linearLine.contents; bool hasPC = (line.addr == ipAddr) && paused; - // Only render enabled breakpoints - bool hasBreakpoint = false; + bool hasEnabledBreakpoint = false; + bool hasDisabledBreakpoint = false; + if (breakpointEnabledMap.count(line.addr) > 0) { - hasBreakpoint = breakpointEnabledMap[line.addr]; + if (breakpointEnabledMap[line.addr]) + hasEnabledBreakpoint = true; + else + hasDisabledBreakpoint = true; } - if (hasPC && hasBreakpoint) + if (hasPC && hasEnabledBreakpoint) { bool appliedTag = false; for (size_t i = 0; i < line.tokens.size(); i++) @@ -201,6 +257,34 @@ void DebuggerRenderLayer::ApplyToHighLevelILBody(Ref function, std::ve line.highlight.b = 0; line.highlight.alpha = 255; } + else if (hasPC && hasDisabledBreakpoint) + { + // PC at a disabled breakpoint - show both indicators, no breakpoint highlighting + bool appliedTag = false; + for (size_t i = 0; i < line.tokens.size(); i++) + { + if (line.tokens[i].type == TagToken) + { + line.tokens[i].text = "🛑︎➞"; + appliedTag = true; + break; + } + } + if (!appliedTag) + { + InstructionTextToken indicator(BNInstructionTextTokenType::TagToken, "🛑︎➞"); + line.tokens.insert(line.tokens.begin(), indicator); + } + + line.highlight.style = StandardHighlightColor; + line.highlight.color = BlueHighlightColor; + line.highlight.mixColor = NoHighlightColor; + line.highlight.mix = 0; + line.highlight.r = 0; + line.highlight.g = 0; + line.highlight.b = 0; + line.highlight.alpha = 255; + } else if (hasPC) { bool appliedTag = false; @@ -228,7 +312,7 @@ void DebuggerRenderLayer::ApplyToHighLevelILBody(Ref function, std::ve line.highlight.b = 0; line.highlight.alpha = 255; } - else if (hasBreakpoint) + else if (hasEnabledBreakpoint) { bool appliedTag = false; for (size_t i = 0; i < line.tokens.size(); i++) @@ -255,6 +339,26 @@ void DebuggerRenderLayer::ApplyToHighLevelILBody(Ref function, std::ve line.highlight.b = 0; line.highlight.alpha = 255; } + else if (hasDisabledBreakpoint) + { + // Disabled breakpoint - show tag but no line highlighting + bool appliedTag = false; + for (size_t i = 0; i < line.tokens.size(); i++) + { + if (line.tokens[i].type == TagToken) + { + line.tokens[i].text = "…🛑︎"; + appliedTag = true; + break; + } + } + if (!appliedTag) + { + InstructionTextToken indicator(BNInstructionTextTokenType::TagToken, "🛑︎"); + line.tokens.insert(line.tokens.begin(), indicator); + } + // No line highlighting for disabled breakpoints + } } } diff --git a/ui/ui.cpp b/ui/ui.cpp index 933e3cec..33417cc3 100644 --- a/ui/ui.cpp +++ b/ui/ui.cpp @@ -811,38 +811,25 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) requireBinaryView)); debuggerMenu->addAction("Toggle Breakpoint", "Breakpoint"); - // Helper function to check if there's an enabled breakpoint at the current address - auto hasEnabledBreakpoint = [](BinaryView* view, uint64_t addr) -> bool { + // Helper function to check if there's a breakpoint at the current address and return its enabled state + auto getBreakpointEnabledState = [](BinaryView* view, uint64_t addr) -> std::pair { auto controller = DebuggerController::GetController(view); if (!controller) - return false; - - std::vector breakpoints = controller->GetBreakpoints(); - for (const auto& bp : breakpoints) - { - if (bp.address == addr) - return bp.enabled; - } - return false; - }; - - // Helper function to check if there's a disabled breakpoint at the current address - auto hasDisabledBreakpoint = [](BinaryView* view, uint64_t addr) -> bool { - auto controller = DebuggerController::GetController(view); - if (!controller) - return false; + return {false, false}; // {hasBreakpoint, isEnabled} std::vector breakpoints = controller->GetBreakpoints(); for (const auto& bp : breakpoints) { if (bp.address == addr) - return !bp.enabled; + return {true, bp.enabled}; } - return false; + return {false, false}; }; - // Register "Enable Breakpoint" action (shown when breakpoint is disabled) + // Register dynamic "Enable/Disable Breakpoint" action UIAction::registerAction("Enable Breakpoint"); + UIAction::registerAction("Disable Breakpoint"); + context->globalActions()->bindAction("Enable Breakpoint", UIAction( [=](const UIActionContext& ctxt) { @@ -852,26 +839,32 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) if (!controller) return; + auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); bool isAbsoluteAddress = controller->IsConnected(); + if (isAbsoluteAddress) { - controller->EnableBreakpoint(ctxt.address); + if (isEnabled) + controller->DisableBreakpoint(ctxt.address); + else + controller->EnableBreakpoint(ctxt.address); } else { std::string filename = controller->GetInputFile(); uint64_t offset = ctxt.address - controller->GetViewFileSegmentsStart(); ModuleNameAndOffset info = {filename, offset}; - controller->EnableBreakpoint(info); + if (isEnabled) + controller->DisableBreakpoint(info); + else + controller->EnableBreakpoint(info); } }, [=](const UIActionContext& ctxt) { - return ctxt.binaryView && hasDisabledBreakpoint(ctxt.binaryView, ctxt.address); + auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); + return ctxt.binaryView && hasBreakpoint && !isEnabled; })); - debuggerMenu->addAction("Enable Breakpoint", "Breakpoint"); - - // Register "Disable Breakpoint" action (shown when breakpoint is enabled) - UIAction::registerAction("Disable Breakpoint"); + context->globalActions()->bindAction("Disable Breakpoint", UIAction( [=](const UIActionContext& ctxt) { @@ -881,24 +874,82 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) if (!controller) return; + auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); bool isAbsoluteAddress = controller->IsConnected(); + if (isAbsoluteAddress) { - controller->DisableBreakpoint(ctxt.address); + if (isEnabled) + controller->DisableBreakpoint(ctxt.address); + else + controller->EnableBreakpoint(ctxt.address); } else { std::string filename = controller->GetInputFile(); uint64_t offset = ctxt.address - controller->GetViewFileSegmentsStart(); ModuleNameAndOffset info = {filename, offset}; - controller->DisableBreakpoint(info); + if (isEnabled) + controller->DisableBreakpoint(info); + else + controller->EnableBreakpoint(info); } }, [=](const UIActionContext& ctxt) { - return ctxt.binaryView && hasEnabledBreakpoint(ctxt.binaryView, ctxt.address); + auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); + return ctxt.binaryView && hasBreakpoint && isEnabled; })); + + debuggerMenu->addAction("Enable Breakpoint", "Breakpoint"); debuggerMenu->addAction("Disable Breakpoint", "Breakpoint"); + // Register "Solo Breakpoint" action + UIAction::registerAction("Solo Breakpoint"); + context->globalActions()->bindAction("Solo Breakpoint", + UIAction( + [=](const UIActionContext& ctxt) { + if (!ctxt.binaryView) + return; + auto controller = DebuggerController::GetController(ctxt.binaryView); + if (!controller) + return; + + // Get the current address breakpoint location + bool isAbsoluteAddress = controller->IsConnected(); + ModuleNameAndOffset currentInfo; + if (!isAbsoluteAddress) + { + std::string filename = controller->GetInputFile(); + uint64_t offset = ctxt.address - controller->GetViewFileSegmentsStart(); + currentInfo = {filename, offset}; + } + + // Disable all breakpoints + std::vector breakpoints = controller->GetBreakpoints(); + for (const auto& bp : breakpoints) + { + ModuleNameAndOffset info; + info.module = bp.module; + info.offset = bp.offset; + controller->DisableBreakpoint(info); + } + + // Enable the current breakpoint + if (isAbsoluteAddress) + { + controller->EnableBreakpoint(ctxt.address); + } + else + { + controller->EnableBreakpoint(currentInfo); + } + }, + [=](const UIActionContext& ctxt) { + auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); + return ctxt.binaryView && hasBreakpoint; + })); + debuggerMenu->addAction("Solo Breakpoint", "Breakpoint"); + UIAction::registerAction("Connect to Debug Server"); context->globalActions()->bindAction("Connect to Debug Server", UIAction( From 6a5ab0d4704a252b0b3cdb39519035b363a27035 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 07:43:33 +0000 Subject: [PATCH 10/12] Merge enable/disable actions into single dynamic action and improve disabled breakpoint visual MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Merge "Enable Breakpoint" and "Disable Breakpoint" into single action using setActionDisplayName for dynamic text 2. Change disabled breakpoint character from 🛑︎ to ⭘ (hollow circle) for better visual distinction 3. Action automatically appears in right-click context menu via globalActions binding Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com> --- ui/renderlayer.cpp | 16 +++++++-------- ui/ui.cpp | 49 ++++++++++++---------------------------------- 2 files changed, 20 insertions(+), 45 deletions(-) diff --git a/ui/renderlayer.cpp b/ui/renderlayer.cpp index 68939c97..8abe3114 100644 --- a/ui/renderlayer.cpp +++ b/ui/renderlayer.cpp @@ -99,14 +99,14 @@ void DebuggerRenderLayer::ApplyToBlock(Ref block, std::vector block, std::vector function, std::ve { if (line.tokens[i].type == TagToken) { - line.tokens[i].text = "🛑︎➞"; + line.tokens[i].text = "⭘➞"; appliedTag = true; break; } } if (!appliedTag) { - InstructionTextToken indicator(BNInstructionTextTokenType::TagToken, "🛑︎➞"); + InstructionTextToken indicator(BNInstructionTextTokenType::TagToken, "⭘➞"); line.tokens.insert(line.tokens.begin(), indicator); } @@ -347,14 +347,14 @@ void DebuggerRenderLayer::ApplyToHighLevelILBody(Ref function, std::ve { if (line.tokens[i].type == TagToken) { - line.tokens[i].text = "…🛑︎"; + line.tokens[i].text = "…⭘"; appliedTag = true; break; } } if (!appliedTag) { - InstructionTextToken indicator(BNInstructionTextTokenType::TagToken, "🛑︎"); + InstructionTextToken indicator(BNInstructionTextTokenType::TagToken, "⭘"); line.tokens.insert(line.tokens.begin(), indicator); } // No line highlighting for disabled breakpoints diff --git a/ui/ui.cpp b/ui/ui.cpp index 33417cc3..ded5177b 100644 --- a/ui/ui.cpp +++ b/ui/ui.cpp @@ -828,7 +828,6 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) // Register dynamic "Enable/Disable Breakpoint" action UIAction::registerAction("Enable Breakpoint"); - UIAction::registerAction("Disable Breakpoint"); context->globalActions()->bindAction("Enable Breakpoint", UIAction( @@ -862,46 +861,22 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context) }, [=](const UIActionContext& ctxt) { auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); - return ctxt.binaryView && hasBreakpoint && !isEnabled; + return ctxt.binaryView && hasBreakpoint; })); - context->globalActions()->bindAction("Disable Breakpoint", - UIAction( - [=](const UIActionContext& ctxt) { - if (!ctxt.binaryView) - return; - auto controller = DebuggerController::GetController(ctxt.binaryView); - if (!controller) - return; - - auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); - bool isAbsoluteAddress = controller->IsConnected(); - - if (isAbsoluteAddress) - { - if (isEnabled) - controller->DisableBreakpoint(ctxt.address); - else - controller->EnableBreakpoint(ctxt.address); - } - else - { - std::string filename = controller->GetInputFile(); - uint64_t offset = ctxt.address - controller->GetViewFileSegmentsStart(); - ModuleNameAndOffset info = {filename, offset}; - if (isEnabled) - controller->DisableBreakpoint(info); - else - controller->EnableBreakpoint(info); - } - }, - [=](const UIActionContext& ctxt) { - auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); - return ctxt.binaryView && hasBreakpoint && isEnabled; - })); + // Dynamically change the action name based on the current breakpoint state + UIAction::setActionDisplayName("Enable Breakpoint", [=](const UIActionContext& ctxt) -> QString { + if (!ctxt.binaryView) + return "Enable Breakpoint"; + + auto [hasBreakpoint, isEnabled] = getBreakpointEnabledState(ctxt.binaryView, ctxt.address); + if (hasBreakpoint && isEnabled) + return "Disable Breakpoint"; + + return "Enable Breakpoint"; + }); debuggerMenu->addAction("Enable Breakpoint", "Breakpoint"); - debuggerMenu->addAction("Disable Breakpoint", "Breakpoint"); // Register "Solo Breakpoint" action UIAction::registerAction("Solo Breakpoint"); From 4b5746a0f2e0fbcdc3aa38f843c3262fd284f4fd Mon Sep 17 00:00:00 2001 From: Xusheng Date: Tue, 21 Oct 2025 15:05:57 +0800 Subject: [PATCH 11/12] Fix UI not updating the breakpoint --- ui/ui.cpp | 64 ++++--------------------------------------------------- 1 file changed, 4 insertions(+), 60 deletions(-) diff --git a/ui/ui.cpp b/ui/ui.cpp index ded5177b..5be5304d 100644 --- a/ui/ui.cpp +++ b/ui/ui.cpp @@ -1709,70 +1709,14 @@ void DebuggerUI::updateUI(const DebuggerEvent& event) } case RelativeBreakpointAddedEvent: - { - uint64_t address = m_controller->RelativeAddressToAbsolute(event.data.relativeAddress); - - std::vector> dataAndAddress; - if (m_controller->GetData()) - dataAndAddress.emplace_back(m_controller->GetData(), address); - - if (DebugModule::IsSameBaseModule(event.data.relativeAddress.module, m_controller->GetInputFile())) - { - dataAndAddress.emplace_back(m_controller->GetData(), m_controller->GetViewFileSegmentsStart() + event.data.relativeAddress.offset); - } - - m_context->refreshCurrentViewContents(); - break; - } case AbsoluteBreakpointAddedEvent: - { - uint64_t address = event.data.absoluteAddress; - - std::vector> dataAndAddress; - BinaryViewRef data = m_controller->GetData(); - if (data) - dataAndAddress.emplace_back(data, address); - - ModuleNameAndOffset relative = m_controller->AbsoluteAddressToRelative(address); - if (DebugModule::IsSameBaseModule(relative.module, m_controller->GetInputFile())) - { - dataAndAddress.emplace_back(m_controller->GetData(), m_controller->GetViewFileSegmentsStart() + relative.offset); - } - - m_context->refreshCurrentViewContents(); - break; - } case RelativeBreakpointRemovedEvent: - { - uint64_t address = m_controller->RelativeAddressToAbsolute(event.data.relativeAddress); - - std::vector> dataAndAddress; - if (m_controller->GetData()) - dataAndAddress.emplace_back(m_controller->GetData(), address); - - if (DebugModule::IsSameBaseModule(event.data.relativeAddress.module, m_controller->GetInputFile())) - { - dataAndAddress.emplace_back(m_controller->GetData(), m_controller->GetViewFileSegmentsStart() + event.data.relativeAddress.offset); - } - - m_context->refreshCurrentViewContents(); - break; - } case AbsoluteBreakpointRemovedEvent: + case RelativeBreakpointEnabledEvent: + case AbsoluteBreakpointEnabledEvent: + case RelativeBreakpointDisabledEvent: + case AbsoluteBreakpointDisabledEvent: { - uint64_t address = event.data.absoluteAddress; - - std::vector> dataAndAddress; - BinaryViewRef data = m_controller->GetData(); - if (data) - dataAndAddress.emplace_back(data, address); - - ModuleNameAndOffset relative = m_controller->AbsoluteAddressToRelative(address); - if (DebugModule::IsSameBaseModule(relative.module, m_controller->GetInputFile())) - { - dataAndAddress.emplace_back(m_controller->GetData(), m_controller->GetViewFileSegmentsStart() + relative.offset); - } - m_context->refreshCurrentViewContents(); break; } From 1e617e5827cbcd7c2a1dafcf2970074614a5e30c Mon Sep 17 00:00:00 2001 From: Xusheng Date: Tue, 21 Oct 2025 17:21:50 +0800 Subject: [PATCH 12/12] Small fix --- ui/uinotification.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/uinotification.cpp b/ui/uinotification.cpp index 87e1bc09..79f4d7cb 100644 --- a/ui/uinotification.cpp +++ b/ui/uinotification.cpp @@ -176,6 +176,8 @@ void NotificationListener::OnContextMenuCreated(UIContext *context, View* view, return; menu.addAction("Debugger", "Toggle Breakpoint", "Breakpoint"); + menu.addAction("Debugger", "Enable Breakpoint", "Breakpoint"); + menu.addAction("Debugger", "Solo Breakpoint", "Breakpoint"); menu.addAction("Debugger", "Launch", "Control"); menu.addAction("Debugger", "Pause", "Control"); menu.addAction("Debugger", "Restart", "Control");