Fix graph export commands to use c api

This commit is contained in:
wargio 2023-02-18 13:35:49 +08:00 committed by Anton Kochkov
parent 20801f7fe6
commit 7a96fad546
6 changed files with 168 additions and 89 deletions

View File

@ -4515,3 +4515,68 @@ QStringList CutterCore::getConfigVariableSpaces(const QString &key)
rz_list_free(list);
return stringList;
}
char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address)
{
CORE_LOCK();
char *string = nullptr;
RzGraph *graph = rz_core_graph(core, type, address);
if (!graph) {
if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph";
} else {
qWarning() << "Cannot get graph at " << RzAddressString(address);
}
return nullptr;
}
core->graph->is_callgraph = type == RZ_CORE_GRAPH_TYPE_FUNCALL;
switch (format) {
case RZ_CORE_GRAPH_FORMAT_CMD: {
string = rz_graph_drawable_to_cmd(graph);
break;
}
case RZ_CORE_GRAPH_FORMAT_DOT: {
string = rz_core_graph_to_dot_str(core, graph);
break;
}
case RZ_CORE_GRAPH_FORMAT_JSON:
/* fall-thru */
case RZ_CORE_GRAPH_FORMAT_JSON_DISASM: {
string = rz_graph_drawable_to_json_str(graph, true);
break;
}
case RZ_CORE_GRAPH_FORMAT_GML: {
string = rz_graph_drawable_to_gml(graph);
break;
}
default:
break;
}
rz_graph_free(graph);
if (!string) {
qWarning() << "Failed to generate graph";
}
return string;
}
void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type,
RVA address)
{
TempConfig tempConfig;
tempConfig.set("scr.color", false);
tempConfig.set("graph.gv.format", format);
CORE_LOCK();
auto filepath = path.toUtf8();
if (!rz_core_graph_write(core, address, type, filepath)) {
if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph";
} else {
qWarning() << "Cannot get graph at " << RzAddressString(address);
}
}
}

View File

@ -720,6 +720,25 @@ public:
*/
bool isWriteModeEnabled();
/**
* @brief Returns the textual version of global or specific graph.
* @param type Graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
* @param format Graph format, example RZ_CORE_GRAPH_FORMAT_DOT or RZ_CORE_GRAPH_FORMAT_GML
* @param address The object address (if global set it to RVA_INVALID)
* @return The textual graph string.
*/
char *getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address);
/**
* @brief Writes a graphviz graph to a file.
* @param path The file output path
* @param format The output format (see graph.gv.format)
* @param type The graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or
* RZ_CORE_GRAPH_TYPE_IMPORT
* @param address The object address (if global set it to RVA_INVALID)
*/
void writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type, RVA address);
signals:
void refreshAll();

View File

@ -7,7 +7,9 @@
#include <QJsonObject>
CallGraphWidget::CallGraphWidget(MainWindow *main, bool global)
: MemoryDockWidget(MemoryWidgetType::CallGraph, main), graphView(new CallGraphView(this, main, global)), global(global)
: MemoryDockWidget(MemoryWidgetType::CallGraph, main),
graphView(new CallGraphView(this, main, global)),
global(global)
{
setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());
this->setWindowTitle(getWindowTitle());
@ -53,7 +55,7 @@ void CallGraphView::showExportDialog()
} else {
defaultName = QString("callgraph_%1").arg(RzAddressString(address));
}
showExportGraphDialog(defaultName, global ? "agC" : "agc", address);
showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_FUNCALL, global ? RVA_INVALID : address);
}
void CallGraphView::showAddress(RVA address)
@ -80,7 +82,6 @@ static inline bool isBetween(ut64 a, ut64 x, ut64 b)
return (a == UT64_MAX || a <= x) && (b == UT64_MAX || x <= b);
}
void CallGraphView::loadCurrentGraph()
{
blockContent.clear();

View File

@ -152,7 +152,7 @@ void CutterGraphView::zoomReset()
void CutterGraphView::showExportDialog()
{
showExportGraphDialog("graph", "", RVA_INVALID);
showExportGraphDialog("global_funcall", RZ_CORE_GRAPH_TYPE_FUNCALL, RVA_INVALID);
}
void CutterGraphView::updateColors()
@ -318,12 +318,12 @@ void CutterGraphView::mouseMoveEvent(QMouseEvent *event)
emit graphMoved();
}
void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QString graphCommand,
RVA address)
void CutterGraphView::exportGraph(QString filePath, GraphExportType exportType,
RzCoreGraphType graphType, RVA address)
{
bool graphTransparent = Config()->getBitmapTransparentState();
double graphScaleFactor = Config()->getBitmapExportScaleFactor();
switch (type) {
switch (exportType) {
case GraphExportType::Png:
this->saveAsBitmap(filePath, "png", graphScaleFactor, graphTransparent);
break;
@ -335,56 +335,55 @@ void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QStrin
break;
case GraphExportType::GVDot:
exportRzTextGraph(filePath, graphCommand + "d", address);
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_DOT, address);
break;
case GraphExportType::RzJson:
exportRzTextGraph(filePath, graphCommand + "j", address);
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_JSON, address);
break;
case GraphExportType::RzGml:
exportRzTextGraph(filePath, graphCommand + "g", address);
break;
case GraphExportType::RzSDBKeyValue:
exportRzTextGraph(filePath, graphCommand + "k", address);
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_GML, address);
break;
case GraphExportType::GVJson:
exportRizinGraphvizGraph(filePath, "json", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "json", graphType, address);
break;
case GraphExportType::GVGif:
exportRizinGraphvizGraph(filePath, "gif", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "gif", graphType, address);
break;
case GraphExportType::GVPng:
exportRizinGraphvizGraph(filePath, "png", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "png", graphType, address);
break;
case GraphExportType::GVJpeg:
exportRizinGraphvizGraph(filePath, "jpg", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "jpg", graphType, address);
break;
case GraphExportType::GVPostScript:
exportRizinGraphvizGraph(filePath, "ps", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "ps", graphType, address);
break;
case GraphExportType::GVSvg:
exportRizinGraphvizGraph(filePath, "svg", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "svg", graphType, address);
break;
case GraphExportType::GVPdf:
Core()->writeGraphvizGraphToFile(filePath, "pdf", graphType, address);
break;
}
}
void CutterGraphView::exportRizinGraphvizGraph(QString filePath, QString type, QString graphCommand,
RVA address)
void CutterGraphView::exportRzTextGraph(QString filePath, RzCoreGraphType type,
RzCoreGraphFormat format, RVA address)
{
TempConfig tempConfig;
tempConfig.set("graph.gv.format", type);
qWarning() << Core()->cmdRawAt(QString("%0w \"%1\"").arg(graphCommand).arg(filePath), address);
}
void CutterGraphView::exportRzTextGraph(QString filePath, QString graphCommand, RVA address)
{
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Can't open file";
char *string = Core()->getTextualGraphAt(type, format, address);
if (!string) {
return;
}
QTextStream fileOut(&file);
fileOut << Core()->cmdRawAt(QString("%0").arg(graphCommand), address);
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream fileOut(&file);
fileOut << string;
} else {
qWarning() << "Can't open or create file: " << filePath;
}
free(string);
}
bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
@ -403,40 +402,39 @@ bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
Q_DECLARE_METATYPE(CutterGraphView::GraphExportType);
void CutterGraphView::showExportGraphDialog(QString defaultName, QString graphCommand, RVA address)
void CutterGraphView::showExportGraphDialog(QString defaultName, RzCoreGraphType type, RVA address)
{
qWarning() << defaultName << " - " << type << " addr " << RzAddressString(address);
QVector<MultitypeFileSaveDialog::TypeDescription> types = {
{ tr("PNG (*.png)"), "png", QVariant::fromValue(GraphExportType::Png) },
{ tr("JPEG (*.jpg)"), "jpg", QVariant::fromValue(GraphExportType::Jpeg) },
{ tr("SVG (*.svg)"), "svg", QVariant::fromValue(GraphExportType::Svg) }
};
bool rzGraphExports = !graphCommand.isEmpty();
if (rzGraphExports) {
types.append({
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
{ tr("Graph Modelling Language (*.gml)"), "gml",
QVariant::fromValue(GraphExportType::RzGml) },
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
{ tr("SDB key-value (*.txt)"), "txt",
QVariant::fromValue(GraphExportType::RzSDBKeyValue) },
});
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|| !QStandardPaths::findExecutable("xdot").isEmpty();
if (hasGraphviz) {
types.append({ { tr("Graphviz json (*.json)"), "json",
QVariant::fromValue(GraphExportType::GVJson) },
{ tr("Graphviz gif (*.gif)"), "gif",
QVariant::fromValue(GraphExportType::GVGif) },
{ tr("Graphviz png (*.png)"), "png",
QVariant::fromValue(GraphExportType::GVPng) },
{ tr("Graphviz jpg (*.jpg)"), "jpg",
QVariant::fromValue(GraphExportType::GVJpeg) },
{ tr("Graphviz PostScript (*.ps)"), "ps",
QVariant::fromValue(GraphExportType::GVPostScript) },
{ tr("Graphviz svg (*.svg)"), "svg",
QVariant::fromValue(GraphExportType::GVSvg) } });
}
types.append({
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
{ tr("Graph Modelling Language (*.gml)"), "gml",
QVariant::fromValue(GraphExportType::RzGml) },
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
});
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|| !QStandardPaths::findExecutable("xdot").isEmpty();
if (hasGraphviz) {
types.append({ { tr("Graphviz json (*.json)"), "json",
QVariant::fromValue(GraphExportType::GVJson) },
{ tr("Graphviz gif (*.gif)"), "gif",
QVariant::fromValue(GraphExportType::GVGif) },
{ tr("Graphviz png (*.png)"), "png",
QVariant::fromValue(GraphExportType::GVPng) },
{ tr("Graphviz jpg (*.jpg)"), "jpg",
QVariant::fromValue(GraphExportType::GVJpeg) },
{ tr("Graphviz PostScript (*.ps)"), "ps",
QVariant::fromValue(GraphExportType::GVPostScript) },
{ tr("Graphviz svg (*.svg)"), "svg",
QVariant::fromValue(GraphExportType::GVSvg) },
{ tr("Graphviz pdf (*.pdf)"), "pdf",
QVariant::fromValue(GraphExportType::GVPdf) } });
}
MultitypeFileSaveDialog dialog(this, tr("Export Graph"));
@ -470,5 +468,5 @@ void CutterGraphView::showExportGraphDialog(QString defaultName, QString graphCo
}
QString filePath = dialog.selectedFiles().first();
exportGraph(filePath, exportType, graphCommand, address);
exportGraph(filePath, exportType, type, address);
}

View File

@ -32,48 +32,38 @@ public:
GVJpeg,
GVPostScript,
GVSvg,
GVPdf,
RzGml,
RzSDBKeyValue,
RzJson
};
/**
* @brief Export graph to a file in the specified format
* @param filePath
* @param type export type, GV* and Rz* types require \p graphCommand
* @param graphCommand rizin graph printing command without type, not required for direct image
* export
* @param address object address for commands like agf
* @param filePath - output file path
* @param exportType - export type, GV* and Rz* types require \p graphCommand
* @param graphType - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or
* RZ_CORE_GRAPH_TYPE_IMPORT
* @param address - object address (if global set it to RVA_INVALID)
*/
void exportGraph(QString filePath, GraphExportType type, QString graphCommand = "",
void exportGraph(QString filePath, GraphExportType exportType, RzCoreGraphType graphType,
RVA address = RVA_INVALID);
/**
* @brief Export image using rizin ag*w command and graphviz.
* Requires graphviz dot executable in the path.
*
* @param filePath output file path
* @param type image format as expected by "e graph.gv.format"
* @param graphCommand rizin command without type, for example agf
* @param address object address if required by command
*/
void exportRizinGraphvizGraph(QString filePath, QString type, QString graphCommand,
RVA address);
/**
* @brief Export graph in one of the text formats supported by rizin json, gml, SDB key-value
* @param filePath output file path
* @param graphCommand graph command including the format, example "agfd" or "agfg"
* @param address object address if required by command
* @param filePath - output file path
* @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
* @param format - graph format, example RZ_CORE_GRAPH_FORMAT_DOT or RZ_CORE_GRAPH_FORMAT_GML
* @param address - object address (if global set it to RVA_INVALID)
*/
void exportRzTextGraph(QString filePath, QString graphCommand, RVA address);
void exportRzTextGraph(QString filePath, RzCoreGraphType type, RzCoreGraphFormat format,
RVA address);
static bool graphIsBitamp(GraphExportType type);
/**
* @brief Show graph export dialog.
* @param defaultName - default file name in the export dialog
* @param graphCommand - rizin graph commmand with graph type and without export type, for
* example afC. Leave empty for non-rizin graphs. In such case only direct image export will be
* available.
* @param address - object address if relevant for \p graphCommand
* @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
* @param address - object address (if global set it to RVA_INVALID)
*/
void showExportGraphDialog(QString defaultName, QString graphCommand = "",
void showExportGraphDialog(QString defaultName, RzCoreGraphType type,
RVA address = RVA_INVALID);
public slots:

View File

@ -194,6 +194,7 @@ void DisassemblerGraphView::loadCurrentGraph()
windowTitle = tr("Graph");
if (fcn && RZ_STR_ISNOTEMPTY(fcn->name)) {
currentFcnAddr = fcn->addr;
auto fcnName = fromOwned(rz_str_escape_utf8_for_json(fcn->name, -1));
windowTitle += QString("(%0)").arg(fcnName.get());
} else {
@ -891,6 +892,11 @@ void DisassemblerGraphView::contextMenuEvent(QContextMenuEvent *event)
void DisassemblerGraphView::showExportDialog()
{
if (currentFcnAddr == RVA_INVALID) {
qWarning() << "Cannot find current function.";
return;
}
QString defaultName = "graph";
if (auto f = Core()->functionIn(currentFcnAddr)) {
QString functionName = f->name;
@ -901,7 +907,7 @@ void DisassemblerGraphView::showExportDialog()
defaultName = functionName;
}
}
showExportGraphDialog(defaultName, "agf", currentFcnAddr);
showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_BLOCK_FUN, currentFcnAddr);
}
void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,