diff options
| author | Mark Rowe <mark@vector35.com> | 2025-07-09 13:36:58 -0700 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2025-07-10 16:08:11 -0700 |
| commit | ebac408aec4bbeab7db3d6336520f5ea2a033a2c (patch) | |
| tree | cc0145ce5392989c8a826e1076404120c63ac264 /examples/triage | |
| parent | d252eb03954d254275ee7981070d8071795073ac (diff) | |
Fix the Triage Summary view's exports list to only update when visible
The `ExportsTreeView` now asks the model to pause / resume updates when
its visibility changes. When paused, all notifications from the view are
ignored. When resumed, notifications set a flag to indicate that an
update is needed and resume the update timer if it is not already
active. The timer is stopped after an update is processed.
There's some extra complexity here to avoid emitting a signal for every
`BinaryView` notification that is processed. These notifications are
typically generated on background threads. The overhead of emitting a
signal and it being routed to the main thread adds up given the number
of notifications involved when loading a large binary.
Diffstat (limited to 'examples/triage')
| -rw-r--r-- | examples/triage/exports.cpp | 68 | ||||
| -rw-r--r-- | examples/triage/exports.h | 23 |
2 files changed, 70 insertions, 21 deletions
diff --git a/examples/triage/exports.cpp b/examples/triage/exports.cpp index 4d274b1a..04547144 100644 --- a/examples/triage/exports.cpp +++ b/examples/triage/exports.cpp @@ -25,13 +25,10 @@ GenericExportsModel::GenericExportsModel(QWidget* parent, BinaryViewRef data): Q } m_updateTimer = new QTimer(this); - m_updateTimer->setSingleShot(true); m_updateTimer->setInterval(500); connect(m_updateTimer, &QTimer::timeout, this, &GenericExportsModel::updateModel); - connect(this, &GenericExportsModel::modelUpdate, this, [=, this]() { - if (m_updateTimer->isActive()) - return; - m_updateTimer->start(); + connect(this, &GenericExportsModel::updateTimerOnUIThread, this, [=, this]() { + updateTimer(m_needsUpdate); }); m_data->RegisterNotification(this); @@ -49,6 +46,10 @@ GenericExportsModel::~GenericExportsModel() void GenericExportsModel::updateModel() { + if (!m_needsUpdate) + return; + + setNeedsUpdate(false); beginResetModel(); m_allEntries.clear(); for (auto& sym : m_data->GetSymbolsOfType(FunctionSymbol)) @@ -237,40 +238,64 @@ void GenericExportsModel::setFilter(const std::string& filterText) endResetModel(); } - -void GenericExportsModel::OnAnalysisFunctionAdded(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) +void GenericExportsModel::setNeedsUpdate(bool needed) { - emit modelUpdate(); + if (m_needsUpdate.exchange(needed) == needed) + return; + + updateTimer(needed); } +void GenericExportsModel::updateTimer(bool needsUpdate) +{ + if (needsUpdate && !m_updateTimer->isActive()) + m_updateTimer->start(); + if (!needsUpdate && m_updateTimer->isActive()) + m_updateTimer->stop(); +} -void GenericExportsModel::OnAnalysisFunctionRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) +void GenericExportsModel::pauseUpdates() { - emit modelUpdate(); + m_updatesPaused = true; + setNeedsUpdate(false); } +void GenericExportsModel::resumeUpdates() +{ + m_updatesPaused = false; + setNeedsUpdate(true); +} -void GenericExportsModel::OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) +void GenericExportsModel::onBinaryViewNotification() { - emit modelUpdate(); + if (m_updatesPaused) + return; + + // This can be called from any thread so we cannot directly + // update the timer. Emitting a signal is relatively expensive + // given how frequently we receive notifications, so we only + // emit a signal if we didn't already need an update. + if (!m_needsUpdate.exchange(true)) + emit updateTimerOnUIThread(); } void GenericExportsModel::OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) { - emit modelUpdate(); + if ((sym->GetBinding() == GlobalBinding) || (sym->GetBinding() == WeakBinding)) + onBinaryViewNotification(); } void GenericExportsModel::OnSymbolUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) { - emit modelUpdate(); + onBinaryViewNotification(); } void GenericExportsModel::OnSymbolRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) { - emit modelUpdate(); + onBinaryViewNotification(); } @@ -403,6 +428,19 @@ void ExportsTreeView::keyPressEvent(QKeyEvent* event) QTreeView::keyPressEvent(event); } +void ExportsTreeView::showEvent(QShowEvent* event) +{ + QTreeView::showEvent(event); + m_model->resumeUpdates(); +} + + +void ExportsTreeView::hideEvent(QHideEvent* event) +{ + QTreeView::hideEvent(event); + m_model->pauseUpdates(); +} + ExportsWidget::ExportsWidget(QWidget* parent, TriageView* view, BinaryViewRef data) : QWidget(parent) { diff --git a/examples/triage/exports.h b/examples/triage/exports.h index 6ab94407..96c2b779 100644 --- a/examples/triage/exports.h +++ b/examples/triage/exports.h @@ -13,16 +13,25 @@ class GenericExportsModel : public QAbstractItemModel, public BinaryNinja::Binar BinaryViewRef m_data; std::vector<SymbolRef> m_allEntries, m_entries; std::string m_filter; + QTimer* m_updateTimer; Qt::SortOrder m_sortOrder; int m_sortCol; bool m_hasOrdinals; - QTimer* m_updateTimer; + + // Read from arbitrary threads while processing notifications. + std::atomic<bool> m_updatesPaused = false; + // Read/written from arbitrary threads while processing notifications. + std::atomic<bool> m_needsUpdate = true; void performSort(int col, Qt::SortOrder order); void updateModel(); - signals: - void modelUpdate(); + void updateTimer(bool); + void setNeedsUpdate(bool); + void onBinaryViewNotification(); + +signals: + void updateTimerOnUIThread(); public: GenericExportsModel(QWidget* parent, BinaryViewRef data); @@ -37,11 +46,11 @@ class GenericExportsModel : public QAbstractItemModel, public BinaryNinja::Binar virtual void sort(int col, Qt::SortOrder order) override; void setFilter(const std::string& filterText); + void pauseUpdates(); + void resumeUpdates(); + SymbolRef getSymbol(const QModelIndex& index); - virtual void OnAnalysisFunctionAdded(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; - virtual void OnAnalysisFunctionRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; - virtual void OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override; virtual void OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override; virtual void OnSymbolUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override; virtual void OnSymbolRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym) override; @@ -75,6 +84,8 @@ class ExportsTreeView : public QTreeView, public FilterTarget protected: virtual void keyPressEvent(QKeyEvent* event) override; + virtual void showEvent(QShowEvent* event) override; + virtual void hideEvent(QHideEvent* event) override; private Q_SLOTS: void exportSelected(const QModelIndex& cur, const QModelIndex& prev); |
