Skip to content

Commit 216f77e

Browse files
committed
[DSC] Refactor the Symbols table to use TriageTablePanel
This gives it asynchronous loading, filtering, and sorting, along with lazy loading and releasing the table data after the view is hidden.
1 parent bcd30a2 commit 216f77e

5 files changed

Lines changed: 290 additions & 296 deletions

File tree

view/sharedcache/api/sharedcache.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ CacheRegion RegionFromApi(BNSharedCacheRegion apiRegion)
5353
region.size = apiRegion.size;
5454
region.flags = apiRegion.flags;
5555
region.type = apiRegion.regionType;
56+
// A zeroed imageStart means the region is not associated with an image.
57+
if (apiRegion.imageStart != 0)
58+
region.imageStart = apiRegion.imageStart;
5659
return region;
5760
}
5861

view/sharedcache/ui/dsctriage.cpp

Lines changed: 59 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ DSCTriageView::DSCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(pare
6060
initStringsTab();
6161
initCacheInfoTables();
6262

63-
// The string scan is expensive, so the panel starts its load only once the Strings tab is
64-
// first shown, and clears its large result set after the tab leaves the screen.
63+
// The string scan and symbol fetch are expensive, so each panel starts its load only once its
64+
// tab is first shown, and clears its large result set after the tab leaves the screen.
6565
connect(m_triageTabs, &SplitTabWidget::currentChanged, this, [this](QWidget* widget) {
6666
m_stringsPanel->setCurrentTabWidget(widget);
67+
m_symbolsPanel->setCurrentTabWidget(widget);
6768
});
6869

6970
m_layout = new QVBoxLayout(this);
@@ -357,59 +358,31 @@ QWidget* DSCTriageView::initImageTable()
357358
void DSCTriageView::initSymbolTable()
358359
{
359360
m_symbolTable = new SymbolTableView(this);
360-
361-
// Apply custom column styling
362-
m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
363-
364-
auto symbolFilterEdit = new FilterEdit(m_symbolTable);
365-
symbolFilterEdit->setPlaceholderText("Filter symbols");
366-
connect(symbolFilterEdit, &FilterEdit::textChanged, [this, symbolFilterEdit](const QString& filter) {
367-
m_symbolTable->setFilter(filter.toStdString(), symbolFilterEdit->getFilterOptions());
368-
});
369-
connect(symbolFilterEdit, &FilterEdit::optionsChanged, [this, symbolFilterEdit](FilterOptions options) {
370-
m_symbolTable->setFilter(symbolFilterEdit->text().toStdString(), options);
361+
m_symbolsPanel = new TriageTablePanel(this, m_symbolTable, "Filter symbols", "symbols");
362+
m_symbolsPanel->setLoader([this] { return startSymbolLoad(); });
363+
m_symbolsPanel->setClearHandler([this] {
364+
// Discard an in-flight symbol fetch rather than letting its results repopulate the
365+
// cleared table.
366+
if (m_symbolsWatcher)
367+
{
368+
m_symbolsWatcher->disconnect();
369+
m_symbolsWatcher->deleteLater();
370+
}
371371
});
372372

373-
auto loadSymbolImageButton = new QPushButton();
373+
m_symbolsPanel->addFilterToggle(":/icons/images/folder.png", "Match Image Names",
374+
[this](bool checked) { m_symbolTable->symbolsModel()->setMatchImageNames(checked); });
375+
376+
auto loadSymbolImageButton = m_symbolsPanel->addSelectionButton("Load Image");
374377
connect(loadSymbolImageButton, &QPushButton::clicked, [this](bool) {
375378
auto selected = m_symbolTable->selectionModel()->selectedRows();
376379
std::vector<uint64_t> addresses;
377380
for (const auto& row : selected)
378-
addresses.push_back(row.data().toString().toULongLong(nullptr, 16));
381+
addresses.push_back(m_symbolTable->getSymbolAtRow(row.row()).address);
379382
loadImagesWithAddr(addresses);
380383
});
381-
loadSymbolImageButton->setText("Load Image");
382-
383-
// Shows the current selected rows image name.
384-
auto currentImageLabel = new QLabel(this);
385-
currentImageLabel->setText("");
386-
currentImageLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
387-
connect(m_symbolTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, [this, currentImageLabel](const QModelIndex &current, const QModelIndex &) {
388-
auto symbol = m_symbolTable->getSymbolAtRow(current.row());
389-
auto controller = SharedCacheController::GetController(*this->m_data);
390-
if (!controller)
391-
return;
392-
auto image = controller->GetImageContaining(symbol.address);
393-
if (image)
394-
currentImageLabel->setText("Image: " + QString::fromStdString(image->name));
395-
else
396-
currentImageLabel->setText("");
397-
});
398-
399-
auto symbolFooterLayout = new QHBoxLayout;
400-
symbolFooterLayout->addWidget(loadSymbolImageButton);
401-
symbolFooterLayout->addWidget(currentImageLabel);
402-
symbolFooterLayout->setAlignment(Qt::AlignLeft);
403-
404-
auto symbolLayout = new QVBoxLayout;
405-
symbolLayout->addWidget(symbolFilterEdit);
406-
symbolLayout->addWidget(m_symbolTable);
407-
symbolLayout->addLayout(symbolFooterLayout);
408-
409-
auto symbolWidget = new QWidget;
410-
symbolWidget->setLayout(symbolLayout);
411384

412-
connect(m_symbolTable, &SymbolTableView::activated, this, [=, this](const QModelIndex& index){
385+
connect(m_symbolTable, &SymbolTableView::activated, this, [this](const QModelIndex& index){
413386
auto symbol = m_symbolTable->getSymbolAtRow(index.row());
414387

415388
auto controller = SharedCacheController::GetController(*this->m_data);
@@ -426,21 +399,43 @@ void DSCTriageView::initSymbolTable()
426399
return;
427400
}
428401

429-
auto dialog = new QMessageBox(this);
430-
dialog->setText("Load " + QString::fromStdString(image->name) + "?");
431-
dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
402+
promptToLoadImage(image->name, image->headerAddress, symbol.address);
403+
});
432404

433-
connect(dialog, &QMessageBox::buttonClicked, this, [=, this](QAbstractButton* button)
434-
{
435-
if (button == dialog->button(QMessageBox::Yes))
436-
loadImagesWithAddr({image->headerAddress}, false, symbol.address);
437-
});
405+
m_triageTabs->addTab(m_symbolsPanel, "Symbols");
406+
m_triageTabs->setCanCloseTab(m_symbolsPanel, false);
407+
}
438408

439-
dialog->exec();
440-
});
441409

442-
m_triageTabs->addTab(symbolWidget, "Symbols");
443-
m_triageTabs->setCanCloseTab(symbolWidget, false);
410+
bool DSCTriageView::startSymbolLoad()
411+
{
412+
// The controller is not available until view init has finished. Retry on the next activation.
413+
auto controller = SharedCacheController::GetController(*m_data);
414+
if (!controller)
415+
return false;
416+
417+
m_symbolTable->setNameSources(*controller);
418+
m_symbolsPanel->statusLabel()->setText("Loading…");
419+
420+
typedef std::vector<CacheSymbol> SymbolList;
421+
QPointer<QFutureWatcher<SymbolList>> watcher = new QFutureWatcher<SymbolList>(this);
422+
m_symbolsWatcher = watcher;
423+
connect(watcher, &QFutureWatcher<SymbolList>::finished, this, [watcher, this]() {
424+
if (!watcher)
425+
return;
426+
m_symbolTable->symbolsModel()->appendSymbols(watcher->result());
427+
m_symbolsPanel->finishLoad();
428+
watcher->deleteLater();
429+
});
430+
QFuture<SymbolList> future = QtConcurrent::run([controller]() { return controller->GetSymbols(); });
431+
watcher->setFuture(future);
432+
connect(this, &QObject::destroyed, this, [watcher]() {
433+
if (watcher && watcher->isRunning()) {
434+
watcher->cancel();
435+
watcher->waitForFinished();
436+
}
437+
});
438+
return true;
444439
}
445440

446441

@@ -537,13 +532,15 @@ void DSCTriageView::showEvent(QShowEvent* event)
537532
{
538533
QWidget::showEvent(event);
539534
m_stringsPanel->setViewVisible(true);
535+
m_symbolsPanel->setViewVisible(true);
540536
}
541537

542538

543539
void DSCTriageView::hideEvent(QHideEvent* event)
544540
{
545541
QWidget::hideEvent(event);
546542
m_stringsPanel->setViewVisible(false);
543+
m_symbolsPanel->setViewVisible(false);
547544
}
548545

549546

@@ -863,6 +860,8 @@ void DSCTriageView::RefreshData()
863860
// TODO: This should use `QSortFilterProxyModel`, but that's a bigger change.
864861
m_mappingTable->setSortingEnabled(true);
865862

866-
867-
m_symbolTable->populateSymbols(*m_data);
863+
// Symbols and strings are loaded lazily when their tabs are shown.
864+
// Discard any loaded content so the reload picks up the refreshed cache information.
865+
m_symbolsPanel->resetContent();
866+
m_stringsPanel->resetContent();
868867
}

view/sharedcache/ui/dsctriage.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#include <QFutureWatcher>
12
#include <QHeaderView>
23
#include <QItemDelegate>
34
#include <QPainter>
5+
#include <QPointer>
46
#include <QSortFilterProxyModel>
57
#include <QStandardItemModel>
68
#include <QStyledItemDelegate>
@@ -193,6 +195,8 @@ class DSCTriageView : public QWidget, public View, public UIContextNotification
193195
QStandardItemModel* m_imageModel;
194196

195197
SymbolTableView* m_symbolTable;
198+
TriageTablePanel* m_symbolsPanel;
199+
QPointer<QFutureWatcher<std::vector<SharedCacheAPI::CacheSymbol>>> m_symbolsWatcher;
196200

197201
StringsTableView* m_stringsTable;
198202
TriageTablePanel* m_stringsPanel;
@@ -230,6 +234,7 @@ class DSCTriageView : public QWidget, public View, public UIContextNotification
230234
void navigateToAddress(uint64_t address);
231235
QWidget* initImageTable();
232236
void initSymbolTable();
237+
bool startSymbolLoad();
233238
void initStringsTab();
234239
bool startStringScan();
235240
void pollStringScan();

0 commit comments

Comments
 (0)