Overview optimization (#1262)

* Overview optimization
* Better cache algorithm
* Fix a bug of the multiple graphs
This commit is contained in:
Vanellope 2019-03-12 16:37:10 +09:00 committed by Itay Cohen
parent 31a832c34d
commit 0be50ac36f
8 changed files with 109 additions and 61 deletions

View File

@ -200,7 +200,10 @@ void MainWindow::initUI()
connect(ui->actionOverview, &QAction::toggled, [this](bool checked) {
if (checked) {
overviewDock->userClosed = false;
adjustOverview();
forceUpdateOverview();
if (targetGraphDock) {
toggleOverview(true, targetGraphDock);
}
}
});
sectionsDock = new SectionsWidget(this, ui->actionSections);
@ -298,34 +301,35 @@ void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph)
return;
}
targetGraphDock = targetGraph;
connect(targetGraphDock->graphView, SIGNAL(refreshBlock()), this, SLOT(adjustOverview()));
connect(targetGraphDock->graphView, SIGNAL(viewRefreshed()), this, SLOT(adjustOverview()));
connect(targetGraphDock->graphView, SIGNAL(viewZoomed()), this, SLOT(adjustOverview()));
connect(targetGraphDock->graphView, SIGNAL(refreshBlock()), this, SLOT(updateOverview()));
connect(targetGraphDock->graphView, SIGNAL(viewRefreshed()), this, SLOT(updateOverview()));
connect(targetGraphDock->graphView, SIGNAL(viewZoomed()), this, SLOT(updateOverview()));
connect(targetGraphDock, &GraphWidget::graphClose, [this]() {
disconnectOverview();
enableOverviewMenu(false);
overviewDock->hide();
});
connect(overviewDock->graphView, SIGNAL(mouseMoved()), this, SLOT(adjustGraph()));
connect(overviewDock, &QDockWidget::dockLocationChanged, this, &MainWindow::adjustOverview);
connect(overviewDock->graphView, SIGNAL(refreshBlock()), this, SLOT(updateOverviewAddr()));
connect(overviewDock, &QDockWidget::dockLocationChanged, this, &MainWindow::forceUpdateOverview);
connect(overviewDock, &OverviewWidget::graphClose, [this]() {
ui->actionOverview->setChecked(false);
if (!core->isGraphEmpty()) {
overviewDock->userClosed = true;
}
});
connect(overviewDock, SIGNAL(resized()), this, SLOT(adjustOverview()));
connect(overviewDock, SIGNAL(resized()), this, SLOT(forceUpdateOverview()));
}
void MainWindow::disconnectOverview()
{
disconnect(targetGraphDock->graphView, SIGNAL(refreshBlock()), this, SLOT(adjustOverview()));
disconnect(targetGraphDock->graphView, SIGNAL(viewRefreshed()), this, SLOT(adjustOverview()));
disconnect(targetGraphDock->graphView, SIGNAL(viewZoomed()), this, SLOT(adjustOverview()));
disconnect(targetGraphDock->graphView, SIGNAL(refreshBlock()), this, SLOT(updateOverview()));
disconnect(targetGraphDock->graphView, SIGNAL(viewRefreshed()), this, SLOT(updateOverview()));
disconnect(targetGraphDock->graphView, SIGNAL(viewZoomed()), this, SLOT(updateOverview()));
disconnect(overviewDock->graphView, SIGNAL(mouseMoved()), this, SLOT(adjustGraph()));
disconnect(overviewDock, &QDockWidget::dockLocationChanged, this, &MainWindow::adjustOverview);
disconnect(overviewDock, SIGNAL(resized()), this, SLOT(adjustOverview()));
disconnect(overviewDock, &QDockWidget::visibilityChanged, this, nullptr);
disconnect(overviewDock->graphView, SIGNAL(refreshBlock()), this, SLOT(updateOverviewAddr()));
disconnect(overviewDock, &QDockWidget::dockLocationChanged, this, &MainWindow::forceUpdateOverview);
disconnect(overviewDock, SIGNAL(resized()), this, SLOT(forceUpdateOverview()));
}
void MainWindow::setOverviewData()
@ -334,17 +338,50 @@ void MainWindow::setOverviewData()
targetGraphDock->graphView->getHeight(), targetGraphDock->graphView->getBlocks());
}
void MainWindow::adjustOverview()
bool MainWindow::isOverviewActive()
{
if (!overviewDock || overviewDock->userClosed) {
return;
return false;
}
if (core->isGraphEmpty()) {
enableOverviewMenu(false);
overviewDock->hide();
return false;
}
return true;
}
void MainWindow::updateOverviewAddr()
{
overviewDock->graphView->currentFcnAddr = targetGraphDock->graphView->currentFcnAddr;
}
void MainWindow::forceUpdateOverview()
{
if (!isOverviewActive()) {
return;
}
overviewDock->graphView->useCache = false;
setOverviewData();
drawOverview();
}
void MainWindow::updateOverview()
{
if (!isOverviewActive()) {
return;
}
if (overviewDock->graphView->currentFcnAddr != targetGraphDock->graphView->currentFcnAddr) {
overviewDock->graphView->useCache = false;
setOverviewData();
} else {
overviewDock->graphView->useCache = true;
}
drawOverview();
}
void MainWindow::drawOverview()
{
qreal curScale = overviewDock->graphView->current_scale;
qreal baseScale = targetGraphDock->graphView->current_scale;
qreal w = targetGraphDock->graphView->viewport()->width() * curScale / baseScale;

View File

@ -137,10 +137,6 @@ public slots:
void openNewFileFailed();
void toggleOverview(bool visibility, GraphWidget *targetGraph);
void disconnectOverview();
void adjustOverview();
void adjustGraph();
private slots:
void on_actionAbout_triggered();
void on_actionIssue_triggered();
@ -193,6 +189,13 @@ private slots:
void changeDebugView();
void changeDefinedView();
void disconnectOverview();
void updateOverview();
void forceUpdateOverview();
void updateOverviewAddr();
void drawOverview();
void adjustGraph();
private:
CutterCore *core;
@ -271,6 +274,7 @@ private:
void updateDockActionsChecked();
void setOverviewData();
bool isOverviewActive();
};
#endif // MAINWINDOW_H

View File

@ -182,6 +182,7 @@ void DisassemblerGraphView::loadCurrentGraph()
QJsonArray functions;
RAnalFunction *fcn = Core()->functionAt(seekable->getOffset());
if (fcn) {
currentFcnAddr = fcn->addr;
QJsonDocument functionsDoc = Core()->cmdj("agJ " + RAddressString(fcn->addr));
functions = functionsDoc.array();
}
@ -211,14 +212,8 @@ void DisassemblerGraphView::loadCurrentGraph()
// Refresh global "empty graph" variable so other widget know there is nothing to show here
Core()->setGraphEmpty(emptyGraph);
Analysis anal;
anal.ready = true;
QJsonValue funcRef = functions.first();
QJsonObject func = funcRef.toObject();
Function f;
f.ready = true;
f.entry = func["offset"].toVariant().toULongLong();
windowTitle = tr("Graph");
QString funcName = func["name"].toString().trimmed();
@ -312,15 +307,10 @@ void DisassemblerGraphView::loadCurrentGraph()
}
disassembly_blocks[db.entry] = db;
prepareGraphNode(gb);
f.blocks.push_back(db);
addBlock(gb);
}
anal.functions[f.entry] = f;
anal.status = "Ready.";
anal.entry = f.entry;
if (!func["blocks"].toArray().isEmpty()) {
computeGraph(entry);
}

View File

@ -83,32 +83,6 @@ class DisassemblerGraphView : public GraphView
bool indirectcall = false;
};
struct Function {
bool ready;
ut64 entry;
ut64 update_id;
std::vector<DisassemblyBlock> blocks;
};
struct Analysis {
ut64 entry = 0;
std::unordered_map<ut64, Function> functions;
bool ready = false;
ut64 update_id = 0;
QString status = "Analyzing...";
bool find_instr(ut64 addr, ut64 &func, ut64 &instr)
{
//TODO implement
Q_UNUSED(addr);
Q_UNUSED(func);
Q_UNUSED(instr);
return false;
}
//dummy class
};
public:
DisassemblerGraphView(QWidget *parent);
~DisassemblerGraphView() override;
@ -129,7 +103,6 @@ public:
int getWidth() { return width; }
int getHeight() { return height; }
std::unordered_map<ut64, GraphBlock> getBlocks() { return blocks; }
public slots:
void refreshView();

View File

@ -381,7 +381,12 @@ QPolygonF GraphView::recalculatePolygon(QPolygonF polygon)
void GraphView::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter p(viewport());
if (useCache) {
drawGraph();
return;
}
pixmap = QPixmap(viewport()->width(), viewport()->height());
QPainter p(&pixmap);
p.setRenderHint(QPainter::Antialiasing);
@ -436,10 +441,18 @@ void GraphView::paintEvent(QPaintEvent *event)
}
}
}
drawGraph();
emit refreshBlock();
}
void GraphView::drawGraph()
{
QRectF target(0.0, 0.0, viewport()->width(), viewport()->height());
QRectF source(0.0, 0.0, viewport()->width(), viewport()->height());
QPainter p(viewport());
p.drawPixmap(target, pixmap, source);
}
// Prepare graph
// This computes the position and (row/col based) size of the block
// Recursively calls itself for each child of the GraphBlock

View File

@ -104,6 +104,18 @@ public:
int offset_x = 0;
int offset_y = 0;
/**
* @brief flag to control if the cached pixmap should be used
*/
bool useCache = false;
/**
* @brief keep the current addr of the fcn of Graph
* Everytime overview updates its contents, it compares this value with the one in Graph
* if they aren't same, then Overview needs to update the pixmap cache.
*/
ut64 currentFcnAddr = 0;
protected:
std::unordered_map<ut64, GraphBlock> blocks;
QColor backgroundColor = QColor(Qt::white);
@ -129,6 +141,7 @@ protected:
virtual void wheelEvent(QWheelEvent *event) override;
virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to);
void drawGraph();
bool event(QEvent *event) override;
// Mouse events
@ -142,6 +155,12 @@ protected:
void centerY();
int width = 0;
int height = 0;
/**
* @brief pixmap that caches the graph nodes
*/
QPixmap pixmap;
private:
bool checkPointClicked(QPointF &point, int x, int y, bool above_y = false);

View File

@ -19,14 +19,14 @@ void OverviewView::setData(int baseWidth, int baseHeight, std::unordered_map<ut6
width = baseWidth;
height = baseHeight;
blocks = baseBlocks;
refreshView();
scaleAndCenter();
}
OverviewView::~OverviewView()
{
}
void OverviewView::refreshView()
void OverviewView::scaleAndCenter()
{
current_scale = (qreal)viewport()->width() / width;
qreal h_scale = (qreal)viewport()->height() / height;
@ -34,6 +34,11 @@ void OverviewView::refreshView()
current_scale = h_scale;
}
center();
}
void OverviewView::refreshView()
{
scaleAndCenter();
viewport()->update();
}
@ -94,6 +99,7 @@ void OverviewView::mousePressEvent(QMouseEvent *event)
qreal x = event->localPos().x() - w / 2;
qreal y = event->localPos().y() - h / 2;
rangeRect = QRectF(x, y, w, h);
useCache = true;
viewport()->update();
emit mouseMoved();
mouseContainsRect(event);
@ -113,6 +119,7 @@ void OverviewView::mouseMoveEvent(QMouseEvent *event)
qreal x = event->localPos().x() - initialDiff.x();
qreal y = event->localPos().y() - initialDiff.y();
rangeRect = QRectF(x, y, rangeRect.width(), rangeRect.height());
useCache = true;
viewport()->update();
emit mouseMoved();
}

View File

@ -36,7 +36,7 @@ public:
public slots:
/**
* @brief refresh the view and adjust the scale
* @brief scale and center all nodes in, then run update
*/
void refreshView();
@ -79,6 +79,11 @@ private:
*/
QPointF initialDiff;
/**
* @brief calculate the scale to fit the all nodes in and center them in the viewport
*/
void scaleAndCenter();
/**
* @brief draw the computed blocks passed by Graph
*/