Skip to content

Commit 8ce4c6d

Browse files
committed
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.
1 parent f3e2a87 commit 8ce4c6d

2 files changed

Lines changed: 82 additions & 14 deletions

File tree

examples/triage/exports.cpp

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,10 @@ GenericExportsModel::GenericExportsModel(QWidget* parent, BinaryViewRef data): Q
2525
}
2626

2727
m_updateTimer = new QTimer(this);
28-
m_updateTimer->setSingleShot(true);
2928
m_updateTimer->setInterval(500);
3029
connect(m_updateTimer, &QTimer::timeout, this, &GenericExportsModel::updateModel);
31-
connect(this, &GenericExportsModel::modelUpdate, this, [=, this]() {
32-
if (m_updateTimer->isActive())
33-
return;
34-
m_updateTimer->start();
30+
connect(this, &GenericExportsModel::updateTimerOnUIThread, this, [=, this]() {
31+
updateTimer(m_needsUpdate);
3532
});
3633

3734
m_data->RegisterNotification(this);
@@ -49,6 +46,10 @@ GenericExportsModel::~GenericExportsModel()
4946

5047
void GenericExportsModel::updateModel()
5148
{
49+
if (!m_needsUpdate)
50+
return;
51+
52+
setNeedsUpdate(false);
5253
beginResetModel();
5354
m_allEntries.clear();
5455
for (auto& sym : m_data->GetSymbolsOfType(FunctionSymbol))
@@ -237,40 +238,80 @@ void GenericExportsModel::setFilter(const std::string& filterText)
237238
endResetModel();
238239
}
239240

241+
void GenericExportsModel::setNeedsUpdate(bool needed)
242+
{
243+
if (m_needsUpdate.exchange(needed) == needed)
244+
return;
245+
246+
updateTimer(needed);
247+
}
248+
249+
void GenericExportsModel::updateTimer(bool needsUpdate)
250+
{
251+
if (needsUpdate && !m_updateTimer->isActive())
252+
m_updateTimer->start();
253+
if (!needsUpdate && m_updateTimer->isActive())
254+
m_updateTimer->stop();
255+
}
256+
257+
void GenericExportsModel::pauseUpdates()
258+
{
259+
m_updatesPaused = true;
260+
setNeedsUpdate(false);
261+
}
262+
263+
void GenericExportsModel::resumeUpdates()
264+
{
265+
m_updatesPaused = false;
266+
setNeedsUpdate(true);
267+
}
268+
269+
void GenericExportsModel::onBinaryViewNotification()
270+
{
271+
if (m_updatesPaused)
272+
return;
273+
274+
// This can be called from any thread so we cannot directly
275+
// update the timer. Emitting a signal is relatively expensive
276+
// given how frequently we receive notifications, so we only
277+
// emit a signal if we didn't already need an update.
278+
if (!m_needsUpdate.exchange(true))
279+
emit updateTimerOnUIThread();
280+
}
240281

241282
void GenericExportsModel::OnAnalysisFunctionAdded(BinaryNinja::BinaryView* view, BinaryNinja::Function* func)
242283
{
243-
emit modelUpdate();
284+
onBinaryViewNotification();
244285
}
245286

246287

247288
void GenericExportsModel::OnAnalysisFunctionRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Function* func)
248289
{
249-
emit modelUpdate();
290+
onBinaryViewNotification();
250291
}
251292

252293

253294
void GenericExportsModel::OnAnalysisFunctionUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Function* func)
254295
{
255-
emit modelUpdate();
296+
onBinaryViewNotification();
256297
}
257298

258299

259300
void GenericExportsModel::OnSymbolAdded(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym)
260301
{
261-
emit modelUpdate();
302+
onBinaryViewNotification();
262303
}
263304

264305

265306
void GenericExportsModel::OnSymbolUpdated(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym)
266307
{
267-
emit modelUpdate();
308+
onBinaryViewNotification();
268309
}
269310

270311

271312
void GenericExportsModel::OnSymbolRemoved(BinaryNinja::BinaryView* view, BinaryNinja::Symbol* sym)
272313
{
273-
emit modelUpdate();
314+
onBinaryViewNotification();
274315
}
275316

276317

@@ -403,6 +444,19 @@ void ExportsTreeView::keyPressEvent(QKeyEvent* event)
403444
QTreeView::keyPressEvent(event);
404445
}
405446

447+
void ExportsTreeView::showEvent(QShowEvent* event)
448+
{
449+
QTreeView::showEvent(event);
450+
m_model->resumeUpdates();
451+
}
452+
453+
454+
void ExportsTreeView::hideEvent(QHideEvent* event)
455+
{
456+
QTreeView::hideEvent(event);
457+
m_model->pauseUpdates();
458+
}
459+
406460

407461
ExportsWidget::ExportsWidget(QWidget* parent, TriageView* view, BinaryViewRef data) : QWidget(parent)
408462
{

examples/triage/exports.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,25 @@ class GenericExportsModel : public QAbstractItemModel, public BinaryNinja::Binar
1313
BinaryViewRef m_data;
1414
std::vector<SymbolRef> m_allEntries, m_entries;
1515
std::string m_filter;
16+
QTimer* m_updateTimer;
1617
Qt::SortOrder m_sortOrder;
1718
int m_sortCol;
1819
bool m_hasOrdinals;
19-
QTimer* m_updateTimer;
20+
21+
// Read from arbitrary threads while processing notifications.
22+
std::atomic<bool> m_updatesPaused = false;
23+
// Read/written from arbitrary threads while processing notifications.
24+
std::atomic<bool> m_needsUpdate = true;
2025

2126
void performSort(int col, Qt::SortOrder order);
2227
void updateModel();
2328

24-
signals:
25-
void modelUpdate();
29+
void updateTimer(bool);
30+
void setNeedsUpdate(bool);
31+
void onBinaryViewNotification();
32+
33+
signals:
34+
void updateTimerOnUIThread();
2635

2736
public:
2837
GenericExportsModel(QWidget* parent, BinaryViewRef data);
@@ -37,6 +46,9 @@ class GenericExportsModel : public QAbstractItemModel, public BinaryNinja::Binar
3746
virtual void sort(int col, Qt::SortOrder order) override;
3847
void setFilter(const std::string& filterText);
3948

49+
void pauseUpdates();
50+
void resumeUpdates();
51+
4052
SymbolRef getSymbol(const QModelIndex& index);
4153

4254
virtual void OnAnalysisFunctionAdded(BinaryNinja::BinaryView* view, BinaryNinja::Function* func) override;
@@ -75,6 +87,8 @@ class ExportsTreeView : public QTreeView, public FilterTarget
7587

7688
protected:
7789
virtual void keyPressEvent(QKeyEvent* event) override;
90+
virtual void showEvent(QShowEvent* event) override;
91+
virtual void hideEvent(QHideEvent* event) override;
7892

7993
private Q_SLOTS:
8094
void exportSelected(const QModelIndex& cur, const QModelIndex& prev);

0 commit comments

Comments
 (0)