cutter/src/widgets/CallGraph.cpp

137 lines
3.9 KiB
C++
Raw Normal View History

#include "CallGraph.h"
#include "MainWindow.h"
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
CallGraphWidget::CallGraphWidget(MainWindow *main, bool global)
2021-01-24 14:50:13 +00:00
: AddressableDockWidget(main), graphView(new CallGraphView(this, main, global)), global(global)
{
setObjectName(main->getUniqueObjectName("CallGraphWidget"));
this->setWindowTitle(getWindowTitle());
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &CallGraphWidget::onSeekChanged);
setWidget(graphView);
}
2021-01-24 14:50:13 +00:00
CallGraphWidget::~CallGraphWidget() {}
QString CallGraphWidget::getWindowTitle() const
{
return global ? tr("Global Callgraph") : tr("Callgraph");
}
void CallGraphWidget::onSeekChanged(RVA address)
{
if (auto function = Core()->functionIn(address)) {
graphView->showAddress(function->addr);
}
}
CallGraphView::CallGraphView(CutterDockWidget *parent, MainWindow *main, bool global)
2021-01-24 14:50:13 +00:00
: SimpleTextGraphView(parent, main), global(global), refreshDeferrer(nullptr, this)
{
enableAddresses(true);
refreshDeferrer.registerFor(parent);
connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this, &CallGraphView::refreshView);
connect(Core(), &CutterCore::refreshAll, this, &SimpleTextGraphView::refreshView);
}
void CallGraphView::showExportDialog()
{
QString defaultName;
if (global) {
defaultName = "global_callgraph";
} else {
2021-09-14 13:32:04 +00:00
defaultName = QString("callgraph_%1").arg(RzAddressString(address));
}
showExportGraphDialog(defaultName, global ? "agC" : "agc", address);
}
void CallGraphView::showAddress(RVA address)
{
if (global) {
auto addressMappingIt = addressMapping.find(address);
if (addressMappingIt != addressMapping.end()) {
selectBlockWithId(addressMappingIt->second);
showBlock(blocks[addressMappingIt->second]);
}
} else if (address != this->address) {
this->address = address;
refreshView();
}
}
void CallGraphView::refreshView()
{
if (!refreshDeferrer.attemptRefresh(nullptr)) {
return;
}
SimpleTextGraphView::refreshView();
}
void CallGraphView::loadCurrentGraph()
{
blockContent.clear();
blocks.clear();
CutterJson nodes = Core()->cmdj(global ? "agCj" : QString("agcj @ %1").arg(address));
QHash<QString, uint64_t> idMapping;
auto getId = [&](const QString &name) -> uint64_t {
auto nextId = idMapping.size();
auto &itemId = idMapping[name];
if (idMapping.size() != nextId) {
itemId = nextId;
}
return itemId;
};
for (CutterJson block : nodes) {
QString name = block["name"].toString();
auto edges = block["imports"];
GraphLayout::GraphBlock layoutBlock;
layoutBlock.entry = getId(name);
for (auto edge : edges) {
auto targetName = edge.toString();
auto targetId = getId(targetName);
layoutBlock.edges.emplace_back(targetId);
}
// it would be good if address came directly from json instead of having to lookup by name
addBlock(std::move(layoutBlock), name, Core()->num(name));
}
for (auto it = idMapping.begin(), end = idMapping.end(); it != end; ++it) {
if (blocks.find(it.value()) == blocks.end()) {
GraphLayout::GraphBlock block;
block.entry = it.value();
addBlock(std::move(block), it.key(), Core()->num(it.key()));
}
}
if (blockContent.empty() && !global) {
2021-09-14 13:32:04 +00:00
addBlock({}, RzAddressString(address), address);
}
addressMapping.clear();
for (auto &it : blockContent) {
addressMapping[it.second.address] = it.first;
}
computeGraphPlacement();
}
void CallGraphView::restoreCurrentBlock()
{
if (!global && lastLoadedAddress != address) {
selectedBlock = NO_BLOCK_SELECTED;
lastLoadedAddress = address;
center();
} else {
SimpleTextGraphView::restoreCurrentBlock();
}
}