2015-01-11 20:01:44 +01:00
|
|
|
/* Copyright radare2 2014-2015 - Author: pancake */
|
2014-04-29 03:53:48 +02:00
|
|
|
|
|
|
|
#include <r_core.h>
|
2015-02-17 01:28:51 +01:00
|
|
|
static const char *mousemodes[] = { "canvas-y", "canvas-x", "node-y", "node-x", NULL };
|
2015-03-05 02:07:53 +01:00
|
|
|
static int mousemode = 0;
|
2015-05-30 01:36:40 +02:00
|
|
|
|
|
|
|
#define BORDER 3
|
|
|
|
#define BORDER_WIDTH 4
|
|
|
|
#define BORDER_HEIGHT 3
|
2015-05-30 18:04:03 +02:00
|
|
|
#define MARGIN_TEXT_X 2
|
|
|
|
#define MARGIN_TEXT_Y 2
|
2015-06-12 11:08:05 +02:00
|
|
|
#define HORIZONTAL_NODE_SPACING 12
|
|
|
|
#define VERTICAL_NODE_SPACING 4
|
2015-05-30 01:36:40 +02:00
|
|
|
#define MAX_NODE_WIDTH 18
|
2015-06-12 11:08:05 +02:00
|
|
|
#define INIT_HISTORY_CAPACITY 16
|
|
|
|
#define TITLE_LEN 128
|
2015-05-30 01:36:40 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
#define history_push(stack, x) (r_stack_push (stack, (void *)(size_t)x))
|
|
|
|
#define history_pop(stack) ((RGraphNode *)r_stack_pop (stack))
|
|
|
|
|
|
|
|
#define gn2addr(sdb,addr,gn) (sdb_num_set (sdb, sdb_fmt (0, "%lld", addr), (ut64)(size_t)gn, 0))
|
|
|
|
#define addr2gn(sdb,addr) ((RGraphNode *)(size_t)sdb_num_get (sdb, sdb_fmt (0, "%lld", addr), NULL))
|
|
|
|
|
|
|
|
#define get_gn(iter) ((RGraphNode *)r_list_iter_get_data(iter))
|
|
|
|
#define get_anode(iter) ((ANode *)get_gn(iter)->data)
|
|
|
|
|
|
|
|
#define graph_foreach_node(list, it, pos, anode) \
|
|
|
|
if (list) for (it = list->head; it && (pos = it->data) && (pos) && (anode = (ANode *)pos->data); it = it->n)
|
2014-05-05 03:15:28 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
typedef struct ascii_node {
|
2014-04-29 03:53:48 +02:00
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
int w;
|
|
|
|
int h;
|
|
|
|
ut64 addr;
|
2014-05-05 03:15:28 +02:00
|
|
|
int depth;
|
2014-07-09 05:05:22 +02:00
|
|
|
char *text;
|
2015-06-12 11:08:05 +02:00
|
|
|
} ANode;
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
typedef struct ascii_graph {
|
2015-06-03 00:18:33 +02:00
|
|
|
RCore *core;
|
|
|
|
RConsCanvas *can;
|
|
|
|
RAnalFunction *fcn;
|
2015-06-12 11:08:05 +02:00
|
|
|
RGraph *graph;
|
|
|
|
RListIter *curnode;
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
int is_callgraph;
|
|
|
|
int is_instep;
|
|
|
|
int is_simple_mode;
|
|
|
|
int is_small_nodes;
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
RStack *history;
|
|
|
|
ANode *update_seek_on;
|
2015-06-03 15:51:08 +02:00
|
|
|
int need_reload_nodes;
|
|
|
|
int force_update_seek;
|
2015-06-12 11:08:05 +02:00
|
|
|
} AGraph;
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
struct agraph_refresh_data {
|
|
|
|
AGraph *g;
|
2015-06-11 12:23:30 +02:00
|
|
|
int fs;
|
2014-04-29 03:53:48 +02:00
|
|
|
};
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
#define G(x,y) r_cons_canvas_gotoxy (g->can, x, y)
|
|
|
|
#define W(x) r_cons_canvas_write (g->can, x)
|
|
|
|
#define B(x,y,w,h) r_cons_canvas_box(g->can, x,y,w,h,NULL)
|
|
|
|
#define B1(x,y,w,h) r_cons_canvas_box(g->can, x,y,w,h,Color_BLUE)
|
|
|
|
#define B2(x,y,w,h) r_cons_canvas_box(g->can, x,y,w,h,Color_MAGENTA)
|
|
|
|
#define L(x,y,x2,y2) r_cons_canvas_line(g->can, x,y,x2,y2,0)
|
|
|
|
#define L1(x,y,x2,y2) r_cons_canvas_line(g->can, x,y,x2,y2,1)
|
|
|
|
#define L2(x,y,x2,y2) r_cons_canvas_line(g->can, x,y,x2,y2,2)
|
|
|
|
#define F(x,y,x2,y2,c) r_cons_canvas_fill(g->can, x,y,x2,y2,c,0)
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void update_node_dimension(RGraph *g, int is_small) {
|
|
|
|
const RList *nodes = r_graph_get_nodes (g);
|
|
|
|
RGraphNode *gn;
|
|
|
|
RListIter *it;
|
|
|
|
ANode *n;
|
2015-05-30 01:21:48 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (nodes, it, gn, n) {
|
2015-06-03 14:47:34 +02:00
|
|
|
if (is_small) {
|
|
|
|
n->w = n->h = 0;
|
|
|
|
} else {
|
|
|
|
n->w = r_str_bounds (n->text, &n->h);
|
|
|
|
n->w += BORDER_WIDTH;
|
|
|
|
n->h += BORDER_HEIGHT;
|
|
|
|
n->w = R_MAX (MAX_NODE_WIDTH, n->w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void small_ANode_print(AGraph *g, ANode *n, int cur) {
|
|
|
|
char title[TITLE_LEN];
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-05-30 12:37:42 +02:00
|
|
|
if (!G (n->x + 2, n->y - 1))
|
2015-05-30 12:16:31 +02:00
|
|
|
return;
|
2015-05-30 12:37:42 +02:00
|
|
|
if (cur) {
|
|
|
|
W("[_@@_]");
|
2015-06-03 00:18:33 +02:00
|
|
|
(void)G (-g->can->sx, -g->can->sy + 2);
|
2015-05-30 12:37:42 +02:00
|
|
|
snprintf (title, sizeof (title) - 1,
|
2015-01-09 11:38:00 +01:00
|
|
|
"0x%08"PFMT64x":", n->addr);
|
2015-05-30 12:37:42 +02:00
|
|
|
W (title);
|
2015-06-03 00:18:33 +02:00
|
|
|
(void)G (-g->can->sx, -g->can->sy + 3);
|
2015-05-30 12:37:42 +02:00
|
|
|
W (n->text);
|
|
|
|
} else {
|
|
|
|
W("[____]");
|
2015-01-09 11:38:00 +01:00
|
|
|
}
|
2015-05-30 12:37:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void normal_ANode_print(AGraph *g, ANode *n, int cur) {
|
|
|
|
char title[TITLE_LEN];
|
2015-05-30 12:37:42 +02:00
|
|
|
char *text;
|
|
|
|
int delta_x = 0;
|
|
|
|
int delta_y = 0;
|
2015-05-31 23:46:32 +02:00
|
|
|
int x, y;
|
2015-01-09 11:38:00 +01:00
|
|
|
|
2015-01-11 01:48:20 +01:00
|
|
|
#if SHOW_OUT_OF_SCREEN_NODES
|
2015-06-03 00:18:33 +02:00
|
|
|
x = n->x + g->can->sx;
|
|
|
|
y = n->y + n->h + g->can->sy;
|
|
|
|
if (x < 0 || x > g->can->w)
|
2015-05-30 01:36:40 +02:00
|
|
|
return;
|
2015-06-03 00:18:33 +02:00
|
|
|
if (y < 0 || y > g->can->h)
|
2015-05-30 01:36:40 +02:00
|
|
|
return;
|
2015-01-11 01:48:20 +01:00
|
|
|
#endif
|
2015-06-03 00:18:33 +02:00
|
|
|
x = n->x + g->can->sx;
|
|
|
|
y = n->y + g->can->sy;
|
2015-05-30 18:04:03 +02:00
|
|
|
if (x < -MARGIN_TEXT_X)
|
|
|
|
delta_x = -x - MARGIN_TEXT_X;
|
|
|
|
if (x + n->w < -MARGIN_TEXT_X)
|
2015-05-30 01:36:40 +02:00
|
|
|
return;
|
|
|
|
if (y < -1)
|
2015-05-30 18:04:03 +02:00
|
|
|
delta_y = -y - MARGIN_TEXT_Y;
|
2015-05-30 01:36:40 +02:00
|
|
|
|
2014-04-29 03:53:48 +02:00
|
|
|
if (cur) {
|
2014-05-18 03:41:39 +02:00
|
|
|
//F (n->x,n->y, n->w, n->h, '.');
|
2014-04-29 03:53:48 +02:00
|
|
|
snprintf (title, sizeof (title)-1,
|
2015-06-12 02:19:58 +02:00
|
|
|
"[0x%08"PFMT64x"]", n->addr);
|
2014-04-29 03:53:48 +02:00
|
|
|
} else {
|
|
|
|
snprintf (title, sizeof (title)-1,
|
2015-06-12 02:19:58 +02:00
|
|
|
" 0x%08"PFMT64x" ", n->addr);
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
2015-05-30 18:16:03 +02:00
|
|
|
if (delta_x < strlen(title) && G(n->x + MARGIN_TEXT_X + delta_x, n->y + 1))
|
|
|
|
W(title + delta_x);
|
2015-05-30 12:37:42 +02:00
|
|
|
|
2015-05-31 23:11:03 +02:00
|
|
|
if (G(n->x + MARGIN_TEXT_X + delta_x, n->y + MARGIN_TEXT_Y + delta_y)) {
|
2015-05-31 22:27:47 +02:00
|
|
|
text = r_str_crop (n->text, delta_x, delta_y, n->w - BORDER_WIDTH, n->h);
|
|
|
|
if (text) {
|
|
|
|
W (text);
|
|
|
|
free (text);
|
|
|
|
} else {
|
|
|
|
W (n->text);
|
|
|
|
}
|
2015-01-11 02:28:59 +01:00
|
|
|
}
|
2015-05-30 12:37:42 +02:00
|
|
|
|
2015-02-15 21:48:46 +01:00
|
|
|
// TODO: check if node is traced or not and hsow proper color
|
2015-06-12 11:08:05 +02:00
|
|
|
// This info must be stored inside ANode* from RCore*
|
2015-02-15 21:48:46 +01:00
|
|
|
if (cur) {
|
|
|
|
B1 (n->x, n->y, n->w, n->h);
|
|
|
|
} else {
|
|
|
|
B (n->x, n->y, n->w, n->h);
|
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void set_layout_bb_depth(AGraph *g, RGraphNode *gn, int depth) {
|
|
|
|
ANode *n;
|
|
|
|
RGraphNode *next;
|
|
|
|
int old_d;
|
|
|
|
if (!gn || !gn->data)
|
2014-05-05 03:15:28 +02:00
|
|
|
return;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
n = gn->data;
|
|
|
|
old_d = n->depth;
|
|
|
|
n->depth = depth;
|
2015-06-03 00:18:33 +02:00
|
|
|
if (old_d != -1)
|
2014-05-05 03:15:28 +02:00
|
|
|
return;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
next = r_graph_nth_neighbour (g->graph, gn, 0);
|
|
|
|
if (next)
|
|
|
|
set_layout_bb_depth (g, next, depth + 1);
|
|
|
|
next = r_graph_nth_neighbour (g->graph, gn, 1);
|
|
|
|
if (next)
|
|
|
|
set_layout_bb_depth (g, next, depth + 1);
|
2014-05-05 03:15:28 +02:00
|
|
|
// TODO: support more than two destination points (switch tables?)
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void set_layout_bb(AGraph *g) {
|
|
|
|
int i, rh, nx;
|
2014-05-05 03:15:28 +02:00
|
|
|
int *rowheight = NULL;
|
|
|
|
int maxdepth = 0;
|
2015-06-12 11:08:05 +02:00
|
|
|
const int h_spacing = HORIZONTAL_NODE_SPACING;
|
|
|
|
const int v_spacing = VERTICAL_NODE_SPACING;
|
|
|
|
const RList *nodes = r_graph_get_nodes (g->graph);
|
|
|
|
RGraphNode *gn;
|
|
|
|
RListIter *it;
|
|
|
|
ANode *n;
|
2014-05-05 03:15:28 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
set_layout_bb_depth (g, (RGraphNode *)r_list_get_bottom(nodes), 0);
|
2014-05-06 01:17:02 +04:00
|
|
|
|
2014-05-05 03:15:28 +02:00
|
|
|
// identify max depth
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (nodes, it, gn, n) {
|
|
|
|
if (n->depth > maxdepth)
|
|
|
|
maxdepth = n->depth;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
|
2014-05-05 03:15:28 +02:00
|
|
|
// identify row height
|
2015-06-03 00:18:33 +02:00
|
|
|
rowheight = malloc (sizeof (int) * maxdepth);
|
|
|
|
for (i = 0; i < maxdepth; i++) {
|
2014-05-05 03:15:28 +02:00
|
|
|
rh = 0;
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (nodes, it, gn, n) {
|
|
|
|
if (n->depth == i)
|
|
|
|
if (n->h > rh)
|
|
|
|
rh = n->h;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
|
|
|
rowheight[i] = rh;
|
|
|
|
}
|
|
|
|
|
|
|
|
// vertical align // depe
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (nodes, it, gn, n) {
|
|
|
|
n->y = 1;
|
|
|
|
for (i = 0; i < n->depth; i++)
|
|
|
|
n->y += rowheight[i] + v_spacing;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
|
|
|
// horitzontal align
|
2015-06-03 00:18:33 +02:00
|
|
|
for (i = 0; i < maxdepth; i++) {
|
|
|
|
nx = (i % 2) * 10;
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (nodes, it, gn, n) {
|
|
|
|
if (n->depth == i) {
|
|
|
|
n->x = nx;
|
|
|
|
nx += n->w + h_spacing;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free (rowheight);
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void set_layout_callgraph(AGraph *g) {
|
|
|
|
const RList *nodes = r_graph_get_nodes (g->graph);
|
|
|
|
RGraphNode *gn;
|
|
|
|
RListIter *it;
|
|
|
|
ANode *prev_n = NULL, *n;
|
2015-06-03 14:59:53 +02:00
|
|
|
int y = 5, x = 20;
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (nodes, it, gn, n) {
|
2015-06-03 14:59:53 +02:00
|
|
|
// wrap to width 'w'
|
2015-06-12 11:08:05 +02:00
|
|
|
if (prev_n && n->x < prev_n->x) {
|
|
|
|
y += 10;
|
|
|
|
x = 0;
|
2015-06-03 14:59:53 +02:00
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
n->x = x;
|
|
|
|
n->y = prev_n ? y : 2;
|
2015-06-03 14:59:53 +02:00
|
|
|
x += 30;
|
2015-06-12 11:08:05 +02:00
|
|
|
prev_n = n;
|
2015-06-03 14:59:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* build the RGraph inside the AGraph g, starting from the Basic Blocks */
|
|
|
|
static int get_bbnodes(AGraph *g) {
|
2014-04-29 03:53:48 +02:00
|
|
|
RAnalBlock *bb;
|
2015-06-03 00:18:33 +02:00
|
|
|
RListIter *iter;
|
2015-06-12 11:08:05 +02:00
|
|
|
Sdb *g_nodes = sdb_new0 ();
|
|
|
|
if (!g_nodes) return R_FALSE;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
r_list_foreach (g->fcn->bbs, iter, bb) {
|
2015-06-12 11:08:05 +02:00
|
|
|
RGraphNode *gn;
|
|
|
|
ANode *node;
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
if (bb->addr == UT64_MAX)
|
|
|
|
continue;
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
node = R_NEW0 (ANode);
|
2015-06-15 21:15:57 +03:00
|
|
|
if (!node) {
|
|
|
|
sdb_free (g_nodes);
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
if (g->is_simple_mode) {
|
2015-06-12 11:08:05 +02:00
|
|
|
node->text = r_core_cmd_strf (g->core,
|
2015-06-03 00:18:33 +02:00
|
|
|
"pI %d @ 0x%08"PFMT64x, bb->size, bb->addr);
|
|
|
|
}else {
|
2015-06-12 11:08:05 +02:00
|
|
|
node->text = r_core_cmd_strf (g->core,
|
2015-06-03 00:18:33 +02:00
|
|
|
"pDi %d @ 0x%08"PFMT64x, bb->size, bb->addr);
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
node->addr = bb->addr;
|
|
|
|
node->depth = -1;
|
|
|
|
node->x = 0;
|
|
|
|
node->y = 0;
|
|
|
|
node->w = 0;
|
|
|
|
node->h = 0;
|
|
|
|
|
|
|
|
gn = r_graph_add_node (g->graph, node);
|
2015-06-15 21:15:57 +03:00
|
|
|
if (!gn) {
|
|
|
|
sdb_free (g_nodes);
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
gn2addr (g_nodes, bb->addr, gn);
|
|
|
|
}
|
|
|
|
|
|
|
|
r_list_foreach (g->fcn->bbs, iter, bb) {
|
|
|
|
RGraphNode *u, *v;
|
|
|
|
if (bb->addr == UT64_MAX)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
u = addr2gn (g_nodes, bb->addr);
|
|
|
|
if (bb->jump != UT64_MAX) {
|
|
|
|
v = addr2gn (g_nodes, bb->jump);
|
|
|
|
r_graph_add_edge (g->graph, u, v);
|
|
|
|
}
|
|
|
|
if (bb->fail != UT64_MAX) {
|
|
|
|
v = addr2gn (g_nodes, bb->fail);
|
|
|
|
r_graph_add_edge (g->graph, u, v);
|
|
|
|
}
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
g->curnode = r_list_iterator (r_graph_get_nodes(g->graph));
|
|
|
|
sdb_free (g_nodes);
|
2015-06-03 00:18:33 +02:00
|
|
|
return R_TRUE;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* build the RGraph inside the AGraph g, starting from the Call Graph
|
|
|
|
* information */
|
|
|
|
static int get_cgnodes(AGraph *g) {
|
2014-09-23 00:40:35 +02:00
|
|
|
#if FCN_OLD
|
2015-06-12 11:08:05 +02:00
|
|
|
Sdb *g_nodes = sdb_new0 ();
|
|
|
|
RGraphNode *fcn_gn;
|
2014-07-09 05:05:22 +02:00
|
|
|
RListIter *iter;
|
2015-06-12 11:08:05 +02:00
|
|
|
RAnalRef *ref;
|
|
|
|
ANode *node;
|
|
|
|
char *code;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
node = R_NEW0 (ANode);
|
2015-06-15 21:15:57 +03:00
|
|
|
if (!node) {
|
|
|
|
sdb_free (g_nodes);
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
node->text = strdup ("");
|
|
|
|
node->addr = g->fcn->addr;
|
|
|
|
node->depth = -1;
|
|
|
|
node->x = 10;
|
|
|
|
node->y = 3;
|
|
|
|
node->w = 0;
|
|
|
|
node->h = 0;
|
|
|
|
fcn_gn = r_graph_add_node (g->graph, node);
|
2015-06-15 21:15:57 +03:00
|
|
|
if (!fcn_gn) {
|
|
|
|
sdb_free (g_nodes);
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
gn2addr (g_nodes, g->fcn->addr, fcn_gn);
|
2015-06-03 12:44:40 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
r_list_foreach (g->fcn->refs, iter, ref) {
|
2015-06-03 12:44:40 +02:00
|
|
|
/* XXX: something is broken, why there are duplicated
|
|
|
|
* nodes here?! goto check fcn->refs!! */
|
2014-07-09 05:05:22 +02:00
|
|
|
/* avoid dups wtf */
|
2015-06-12 11:08:05 +02:00
|
|
|
RGraphNode *gn;
|
|
|
|
gn = addr2gn (g_nodes, ref->addr);
|
|
|
|
if (gn) continue;
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
RFlagItem *fi = r_flag_get_at (g->core->flags, ref->addr);
|
2015-06-12 11:08:05 +02:00
|
|
|
node = R_NEW0 (ANode);
|
|
|
|
if (!node) return R_FALSE;
|
2014-07-09 05:05:22 +02:00
|
|
|
if (fi) {
|
2015-06-12 11:08:05 +02:00
|
|
|
node->text = strdup (fi->name);
|
|
|
|
node->text = r_str_concat (node->text, ":\n");
|
2014-07-09 05:05:22 +02:00
|
|
|
} else {
|
2015-06-12 11:08:05 +02:00
|
|
|
node->text = strdup ("");
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
code = r_core_cmd_strf (g->core,
|
2014-07-09 05:05:22 +02:00
|
|
|
"pi 4 @ 0x%08"PFMT64x, ref->addr);
|
2015-06-12 11:08:05 +02:00
|
|
|
node->text = r_str_concat (node->text, code);
|
|
|
|
node->text = r_str_concat (node->text, "...\n");
|
|
|
|
node->addr = ref->addr;
|
|
|
|
node->depth = -1;
|
|
|
|
node->x = 10;
|
|
|
|
node->y = 10;
|
|
|
|
node->w = 0;
|
|
|
|
node->h = 0;
|
2014-07-09 05:05:22 +02:00
|
|
|
free (code);
|
2015-06-12 11:08:05 +02:00
|
|
|
gn = r_graph_add_node (g->graph, node);
|
2015-06-15 21:15:57 +03:00
|
|
|
if (!gn) {
|
|
|
|
sdb_free (g_nodes);
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
gn2addr (g_nodes, ref->addr, gn);
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
r_graph_add_edge (g->graph, fcn_gn, gn);
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
g->curnode = r_list_iterator (r_graph_get_nodes (g->graph));
|
|
|
|
sdb_free (g_nodes);
|
2014-09-23 00:40:35 +02:00
|
|
|
#else
|
2015-06-12 11:08:05 +02:00
|
|
|
eprintf ("Must be sdbized\n");
|
2014-09-23 00:40:35 +02:00
|
|
|
#endif
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
return R_TRUE;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static int reload_nodes(AGraph *g) {
|
2015-06-03 14:59:53 +02:00
|
|
|
int ret;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-03 11:38:31 +02:00
|
|
|
if (g->is_callgraph) {
|
2015-06-03 15:25:05 +02:00
|
|
|
ret = get_cgnodes(g);
|
2015-06-03 11:38:31 +02:00
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
|
|
|
} else {
|
2015-06-03 15:25:05 +02:00
|
|
|
ret = get_bbnodes(g);
|
2015-06-03 11:38:31 +02:00
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
2015-06-03 11:38:31 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
update_node_dimension(g->graph, g->is_small_nodes);
|
2015-06-03 14:59:53 +02:00
|
|
|
return R_TRUE;
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void update_seek(RConsCanvas *can, ANode *n, int force) {
|
2015-06-03 15:25:05 +02:00
|
|
|
int x, y, w, h;
|
|
|
|
int doscroll = R_FALSE;
|
|
|
|
|
|
|
|
if (!n) return;
|
|
|
|
|
|
|
|
x = n->x + can->sx;
|
|
|
|
y = n->y + can->sy;
|
|
|
|
w = can->w;
|
|
|
|
h = can->h;
|
|
|
|
|
|
|
|
doscroll = force || y < 0 || y + 5 > h || x + 5 > w || x + n->w + 5 < 0;
|
|
|
|
|
|
|
|
if (doscroll) {
|
|
|
|
// top-left
|
|
|
|
can->sy = -n->y + BORDER;
|
|
|
|
can->sx = -n->x + BORDER;
|
|
|
|
// center
|
|
|
|
can->sy = -n->y + BORDER + (h / 8);
|
|
|
|
can->sx = -n->x + BORDER + (w / 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_set_layout(AGraph *g) {
|
2015-06-03 15:51:08 +02:00
|
|
|
if (g->is_callgraph)
|
|
|
|
set_layout_callgraph(g);
|
|
|
|
else
|
|
|
|
set_layout_bb(g);
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* set the willing to center the screen on a particular node */
|
|
|
|
static void agraph_update_seek(AGraph *g, ANode *n, int force) {
|
|
|
|
g->update_seek_on = n;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->force_update_seek = force;
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_free(AGraph *g) {
|
|
|
|
r_graph_free (g->graph);
|
|
|
|
r_stack_free (g->history);
|
2015-06-03 15:25:05 +02:00
|
|
|
free(g);
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_print_node(AGraph *g, ANode *n) {
|
|
|
|
const int cur = get_anode (g->curnode) == n;
|
2015-06-03 15:25:05 +02:00
|
|
|
|
|
|
|
if (g->is_small_nodes)
|
2015-06-12 11:08:05 +02:00
|
|
|
small_ANode_print(g, n, cur);
|
2015-06-03 15:25:05 +02:00
|
|
|
else
|
2015-06-12 11:08:05 +02:00
|
|
|
normal_ANode_print(g, n, cur);
|
2015-06-03 15:25:05 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_print_nodes(AGraph *g) {
|
|
|
|
const RList *nodes = r_graph_get_nodes (g->graph);
|
|
|
|
RGraphNode *gn;
|
|
|
|
RListIter *it;
|
|
|
|
ANode *n;
|
|
|
|
|
|
|
|
graph_foreach_node (nodes, it, gn, n) {
|
|
|
|
if (gn != get_gn (g->curnode))
|
|
|
|
agraph_print_node(g, n);
|
|
|
|
}
|
2015-06-03 15:25:05 +02:00
|
|
|
|
|
|
|
/* draw current node now to make it appear on top */
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_print_node (g, get_anode(g->curnode));
|
2015-06-03 15:25:05 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* print an edge between two nodes.
|
|
|
|
* nth: specifies if the edge is the true(1)/false(2) branch or if it's the
|
|
|
|
* only edge for that node(0), so that a different style will be applied
|
|
|
|
* to the drawn line */
|
|
|
|
static void agraph_print_edge(AGraph *g, ANode *a, ANode *b, int nth) {
|
2015-06-03 15:25:05 +02:00
|
|
|
int x, y, x2, y2;
|
|
|
|
int xinc = 3 + 2 * (nth + 1);
|
|
|
|
x = a->x + xinc;
|
|
|
|
y = a->y + a->h;
|
|
|
|
x2 = b->x + xinc;
|
|
|
|
y2 = b->y;
|
|
|
|
if (a == b) {
|
|
|
|
x2 = a->x;
|
|
|
|
y2 = y - 3;
|
|
|
|
}
|
|
|
|
switch (nth) {
|
|
|
|
case 0: L1 (x, y, x2, y2); break;
|
|
|
|
case 1: L2 (x, y, x2, y2); break;
|
|
|
|
case -1: L (x, y, x2, y2); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_print_edges(AGraph *g) {
|
|
|
|
const RList *nodes = r_graph_get_nodes (g->graph);
|
|
|
|
RGraphNode *gn, *gv;
|
|
|
|
RListIter *it, *itn;
|
|
|
|
ANode *u, *v;
|
2015-06-03 15:25:05 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (nodes, it, gn, u) {
|
|
|
|
const RList *neighbours = r_graph_get_neighbours (g->graph, gn);
|
|
|
|
const int exit_edges = r_list_length (neighbours);
|
|
|
|
int nth = 0;
|
2015-06-03 15:25:05 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
graph_foreach_node (neighbours, itn, gv, v) {
|
|
|
|
int cur_nth = nth;
|
|
|
|
if (g->is_callgraph) {
|
|
|
|
/* hack: we don't support more than two exit edges from a node
|
|
|
|
* yet, so set nth to zero, to make every edge appears as the
|
|
|
|
* "true" edge of the node */
|
|
|
|
cur_nth = 0;
|
|
|
|
} else if (exit_edges == 1) {
|
|
|
|
cur_nth = -1;
|
|
|
|
}
|
|
|
|
agraph_print_edge (g, u, v, cur_nth);
|
|
|
|
nth++;
|
2015-06-03 15:25:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_toggle_small_nodes(AGraph *g) {
|
2015-06-03 15:25:05 +02:00
|
|
|
g->is_small_nodes = !g->is_small_nodes;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->need_reload_nodes = R_TRUE;
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_toggle_simple_mode(AGraph *g) {
|
2015-06-03 15:51:08 +02:00
|
|
|
g->is_simple_mode = !g->is_simple_mode;
|
|
|
|
g->need_reload_nodes = R_TRUE;
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_toggle_callgraph(AGraph *g) {
|
2015-06-03 15:51:08 +02:00
|
|
|
g->is_callgraph = !g->is_callgraph;
|
|
|
|
g->need_reload_nodes = R_TRUE;
|
2015-06-03 15:25:05 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* reload all the info in the nodes, depending on the type of the graph
|
|
|
|
* (callgraph, CFG, etc.), set the default layout for these nodes and center
|
|
|
|
* the screen on the selected one */
|
|
|
|
static int agraph_reload_nodes(AGraph *g) {
|
2015-06-03 14:59:53 +02:00
|
|
|
int ret;
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
r_graph_reset (g->graph);
|
2015-06-03 14:59:53 +02:00
|
|
|
ret = reload_nodes(g);
|
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_set_layout(g);
|
|
|
|
g->update_seek_on = get_anode(g->curnode);
|
2015-06-03 00:18:33 +02:00
|
|
|
return R_TRUE;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void follow_nth(AGraph *g, int nth) {
|
|
|
|
const RGraphNode *cn = r_graph_nth_neighbour (g->graph, get_gn(g->curnode), nth);
|
|
|
|
if (cn) {
|
|
|
|
history_push (g->history, get_gn (g->curnode));
|
|
|
|
g->curnode = r_graph_node_iter (g->graph, cn->idx);
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
2015-05-30 01:21:48 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_follow_true(AGraph *g) {
|
2015-06-03 00:18:33 +02:00
|
|
|
follow_nth(g, 0);
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_update_seek(g, get_anode(g->curnode), R_FALSE);
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_follow_false(AGraph *g) {
|
2015-06-03 00:18:33 +02:00
|
|
|
follow_nth(g, 1);
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_update_seek(g, get_anode(g->curnode), R_FALSE);
|
2015-02-14 05:10:03 +01:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* go back in the history of selected nodes, if we can */
|
|
|
|
static void agraph_undo_node(AGraph *g) {
|
|
|
|
const RGraphNode *p = history_pop (g->history);
|
|
|
|
if (p) {
|
|
|
|
g->curnode = r_graph_node_iter (g->graph, p->idx);
|
|
|
|
agraph_update_seek (g, p->data, R_FALSE);
|
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* pushes the current node in the history and makes g->curnode the next node in
|
|
|
|
* the order given by r_graph_get_nodes */
|
|
|
|
static void agraph_next_node(AGraph *g) {
|
|
|
|
if (!g->curnode->n) return;
|
|
|
|
history_push (g->history, get_gn(g->curnode));
|
|
|
|
g->curnode = g->curnode->n;
|
|
|
|
agraph_update_seek (g, get_anode(g->curnode), R_FALSE);
|
2015-06-03 11:38:31 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* pushes the current node in the history and makes g->curnode the prev node in
|
|
|
|
* the order given by r_graph_get_nodes */
|
|
|
|
static void agraph_prev_node(AGraph *g) {
|
|
|
|
if (!g->curnode->p) return;
|
|
|
|
history_push (g->history, get_gn(g->curnode));
|
|
|
|
g->curnode = g->curnode->p;
|
|
|
|
agraph_update_seek (g, get_anode(g->curnode), R_FALSE);
|
2015-06-03 11:38:31 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static int agraph_refresh(struct agraph_refresh_data *grd) {
|
|
|
|
char title[TITLE_LEN];
|
|
|
|
AGraph *g = grd->g;
|
|
|
|
const int fs = grd->fs;
|
2015-06-03 14:20:03 +02:00
|
|
|
int h, w = r_cons_get_size (&h);
|
2015-06-03 15:51:08 +02:00
|
|
|
int ret;
|
2015-06-03 12:24:42 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
/* allow to change the current function only during debugging */
|
2015-06-03 00:18:33 +02:00
|
|
|
if (g->is_instep && g->core->io->debug) {
|
2015-05-30 01:36:40 +02:00
|
|
|
RAnalFunction *f;
|
2015-06-03 00:18:33 +02:00
|
|
|
r_core_cmd0 (g->core, "sr pc");
|
|
|
|
f = r_anal_get_fcn_in (g->core->anal, g->core->offset, 0);
|
|
|
|
if (f && f != g->fcn) {
|
|
|
|
g->fcn = f;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->need_reload_nodes = R_TRUE;
|
2014-10-23 03:29:05 +02:00
|
|
|
}
|
|
|
|
}
|
2015-06-03 15:51:08 +02:00
|
|
|
|
|
|
|
/* look for any change in the state of the graph
|
|
|
|
* and update what's necessary */
|
|
|
|
if (g->need_reload_nodes) {
|
2015-06-12 11:08:05 +02:00
|
|
|
ret = agraph_reload_nodes(g);
|
2015-06-03 15:51:08 +02:00
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
|
|
|
|
|
|
|
g->need_reload_nodes = R_FALSE;
|
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
if (g->update_seek_on) {
|
|
|
|
update_seek(g->can, g->update_seek_on, g->force_update_seek);
|
|
|
|
g->update_seek_on = NULL;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->force_update_seek = R_FALSE;
|
|
|
|
}
|
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
if (fs) {
|
|
|
|
r_cons_clear00 ();
|
|
|
|
}
|
2015-05-30 01:36:40 +02:00
|
|
|
|
2015-06-11 12:23:30 +02:00
|
|
|
h = fs ? h : 1024;
|
|
|
|
r_cons_canvas_resize (g->can, w, h);
|
2015-06-03 00:18:33 +02:00
|
|
|
r_cons_canvas_clear (g->can);
|
2014-08-04 01:29:53 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_print_edges(g);
|
|
|
|
agraph_print_nodes(g);
|
2014-08-04 01:29:53 +02:00
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
if (fs) {
|
|
|
|
(void)G (-g->can->sx, -g->can->sy);
|
|
|
|
snprintf (title, sizeof (title)-1,
|
|
|
|
"[0x%08"PFMT64x"]> %d VV @ %s (nodes %d edges %d) %s mouse:%s",
|
2015-06-12 11:08:05 +02:00
|
|
|
g->fcn->addr, r_stack_size (g->history), g->fcn->name,
|
|
|
|
g->graph->n_nodes, g->graph->n_edges, g->is_callgraph?"CG":"BB",
|
2015-06-11 03:55:15 +02:00
|
|
|
mousemodes[mousemode]);
|
|
|
|
W (title);
|
|
|
|
}
|
2014-08-04 01:29:53 +02:00
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
if (fs) {
|
|
|
|
r_cons_canvas_print (g->can);
|
|
|
|
} else {
|
|
|
|
r_cons_canvas_print_region (g->can);
|
|
|
|
}
|
|
|
|
if (fs) {
|
|
|
|
const char *cmdv = r_config_get (g->core->config, "cmd.gprompt");
|
|
|
|
if (cmdv && *cmdv) {
|
|
|
|
r_cons_gotoxy (0, 1);
|
|
|
|
r_core_cmd0 (g->core, cmdv);
|
|
|
|
}
|
2014-10-23 03:29:05 +02:00
|
|
|
}
|
2015-06-04 01:30:33 +02:00
|
|
|
r_cons_flush_nonewline ();
|
2015-06-03 00:18:33 +02:00
|
|
|
return R_TRUE;
|
2014-08-04 01:29:53 +02:00
|
|
|
}
|
2014-09-26 15:40:17 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static void agraph_init(AGraph *g) {
|
2015-06-03 00:18:33 +02:00
|
|
|
g->is_callgraph = R_FALSE;
|
|
|
|
g->is_instep = R_FALSE;
|
|
|
|
g->is_simple_mode = R_TRUE;
|
|
|
|
g->is_small_nodes = R_FALSE;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->need_reload_nodes = R_TRUE;
|
2015-06-12 11:08:05 +02:00
|
|
|
g->curnode = NULL;
|
|
|
|
g->update_seek_on = NULL;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->force_update_seek = R_TRUE;
|
2015-06-12 11:08:05 +02:00
|
|
|
g->history = r_stack_new (INIT_HISTORY_CAPACITY);
|
|
|
|
g->graph = r_graph_new ();
|
2014-10-23 03:29:05 +02:00
|
|
|
}
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
static AGraph *agraph_new(RCore *core, RConsCanvas *can, RAnalFunction *fcn) {
|
|
|
|
AGraph *g;
|
2015-04-11 02:41:02 +03:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
g = (AGraph *)malloc(sizeof(AGraph));
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!g)
|
2015-06-03 15:51:08 +02:00
|
|
|
return NULL;
|
2015-04-11 02:41:02 +03:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
g->core = core;
|
|
|
|
g->can = can;
|
|
|
|
g->fcn = fcn;
|
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_init(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
return g;
|
2014-10-23 02:13:25 +02:00
|
|
|
}
|
|
|
|
|
2015-06-11 12:23:30 +02:00
|
|
|
R_API int r_core_visual_graph(RCore *core, RAnalFunction *_fcn, int is_interactive) {
|
2015-06-12 02:19:58 +02:00
|
|
|
int exit_graph = R_FALSE, is_error = R_FALSE;
|
2015-06-12 11:08:05 +02:00
|
|
|
struct agraph_refresh_data *grd;
|
2015-06-12 02:19:58 +02:00
|
|
|
int okey, key, wheel;
|
2015-06-03 00:18:33 +02:00
|
|
|
RAnalFunction *fcn;
|
2015-06-12 02:19:58 +02:00
|
|
|
const char *key_s;
|
2015-06-03 00:18:33 +02:00
|
|
|
RConsCanvas *can;
|
2015-06-12 11:08:05 +02:00
|
|
|
AGraph *g;
|
2015-02-17 01:28:51 +01:00
|
|
|
int wheelspeed;
|
2015-06-03 00:18:33 +02:00
|
|
|
int w, h;
|
2015-06-12 02:19:58 +02:00
|
|
|
int ret;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2014-09-26 18:10:33 +02:00
|
|
|
fcn = _fcn? _fcn: r_anal_get_fcn_in (core->anal, core->offset, 0);
|
2014-07-09 05:05:22 +02:00
|
|
|
if (!fcn) {
|
|
|
|
eprintf ("No function in current seek\n");
|
|
|
|
return R_FALSE;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
2014-07-09 05:05:22 +02:00
|
|
|
w = r_cons_get_size (&h);
|
2015-06-11 12:23:30 +02:00
|
|
|
can = r_cons_canvas_new (w, h);
|
2015-06-02 23:50:02 +02:00
|
|
|
if (!can) {
|
|
|
|
eprintf ("Cannot create RCons.canvas context\n");
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
2015-02-15 21:34:12 +01:00
|
|
|
can->linemode = 1;
|
2015-02-14 04:50:29 +01:00
|
|
|
can->color = r_config_get_i (core->config, "scr.color");
|
|
|
|
// disable colors in disasm because canvas doesnt supports ansi text yet
|
|
|
|
r_config_set_i (core->config, "scr.color", 0);
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
g = agraph_new (core, can, fcn);
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!g) {
|
|
|
|
is_error = R_TRUE;
|
|
|
|
goto err_graph_new;
|
2014-08-03 14:43:44 +02:00
|
|
|
}
|
2014-05-05 03:15:28 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
grd = (struct agraph_refresh_data *)malloc (sizeof(*grd));
|
2015-06-11 12:23:30 +02:00
|
|
|
grd->g = g;
|
|
|
|
grd->fs = is_interactive;
|
|
|
|
|
|
|
|
core->cons->event_data = grd;
|
2015-06-12 11:08:05 +02:00
|
|
|
core->cons->event_resize = (RConsEvent)agraph_refresh;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
while (!exit_graph && !is_error) {
|
2015-05-30 01:36:40 +02:00
|
|
|
w = r_cons_get_size (&h);
|
2015-06-12 11:08:05 +02:00
|
|
|
ret = agraph_refresh (grd);
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!ret) {
|
|
|
|
is_error = R_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-11 12:23:30 +02:00
|
|
|
if (!is_interactive) {
|
|
|
|
/* this is a non-interactive ascii-art graph, so exit the loop */
|
|
|
|
r_cons_printf (Color_RESET);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-04 01:30:33 +02:00
|
|
|
r_cons_show_cursor(R_FALSE);
|
2015-05-30 01:36:40 +02:00
|
|
|
wheel = r_config_get_i (core->config, "scr.wheel");
|
|
|
|
if (wheel)
|
|
|
|
r_cons_enable_mouse (R_TRUE);
|
|
|
|
|
|
|
|
// r_core_graph_inputhandle()
|
|
|
|
okey = r_cons_readchar ();
|
|
|
|
key = r_cons_arrow_to_hjkl (okey);
|
|
|
|
if (r_cons_singleton()->mouse_event) {
|
|
|
|
wheelspeed = r_config_get_i (core->config, "scr.wheelspeed");
|
2014-07-09 05:05:22 +02:00
|
|
|
} else {
|
2015-05-30 01:36:40 +02:00
|
|
|
wheelspeed = 1;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-05-30 01:36:40 +02:00
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case '=':
|
|
|
|
case '|':
|
|
|
|
{ // TODO: edit
|
|
|
|
const char *buf = NULL;
|
|
|
|
const char *cmd = r_config_get (core->config, "cmd.gprompt");
|
|
|
|
r_line_set_prompt ("cmd.gprompt> ");
|
|
|
|
core->cons->line->contents = strdup (cmd);
|
|
|
|
buf = r_line_readline ();
|
|
|
|
core->cons->line->contents = NULL;
|
|
|
|
r_config_set (core->config, "cmd.gprompt", buf);
|
2015-02-17 01:28:51 +01:00
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'O':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_toggle_simple_mode(g);
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'V':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_toggle_callgraph(g);
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'z':
|
2015-06-03 00:18:33 +02:00
|
|
|
g->is_instep = R_TRUE;
|
2015-06-12 02:19:58 +02:00
|
|
|
key_s = r_config_get (core->config, "key.s");
|
|
|
|
if (key_s && *key_s) {
|
|
|
|
r_core_cmd0 (core, key_s);
|
|
|
|
} else {
|
|
|
|
if (r_config_get_i (core->config, "cfg.debug"))
|
|
|
|
r_core_cmd0 (core, "ds;.dr*");
|
|
|
|
else
|
|
|
|
r_core_cmd0 (core, "aes;.dr*");
|
|
|
|
}
|
2015-06-12 11:08:05 +02:00
|
|
|
ret = agraph_reload_nodes(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!ret)
|
|
|
|
is_error = R_TRUE;
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'Z':
|
|
|
|
if (okey == 27) {
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_prev_node(g);
|
2015-05-30 01:36:40 +02:00
|
|
|
} else {
|
|
|
|
// 'Z'
|
2015-06-03 00:18:33 +02:00
|
|
|
g->is_instep = R_TRUE;
|
2015-05-30 01:36:40 +02:00
|
|
|
if (r_config_get_i (core->config, "cfg.debug"))
|
|
|
|
r_core_cmd0 (core, "dso;.dr*");
|
2015-06-03 11:38:31 +02:00
|
|
|
else
|
|
|
|
r_core_cmd0 (core, "aeso;.dr*");
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
ret = agraph_reload_nodes(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!ret)
|
|
|
|
is_error = R_TRUE;
|
2015-05-30 01:36:40 +02:00
|
|
|
}
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'x':
|
|
|
|
if (r_core_visual_xrefs_x (core))
|
2015-06-03 00:18:33 +02:00
|
|
|
exit_graph = R_TRUE;
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'X':
|
|
|
|
if (r_core_visual_xrefs_X (core))
|
2015-06-03 00:18:33 +02:00
|
|
|
exit_graph = R_TRUE;
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 9: // tab
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_next_node(g);
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case '?':
|
|
|
|
r_cons_clear00 ();
|
|
|
|
r_cons_printf ("Visual Ascii Art graph keybindings:\n"
|
|
|
|
" . - center graph to the current node\n"
|
|
|
|
" C - toggle scr.color\n"
|
|
|
|
" hjkl - move node\n"
|
|
|
|
" asdw - scroll canvas\n"
|
|
|
|
" tab - select next node\n"
|
|
|
|
" TAB - select previous node\n"
|
|
|
|
" t/f - follow true/false edges\n"
|
|
|
|
" e - toggle edge-lines style (diagonal/square)\n"
|
|
|
|
" O - toggle disasm mode\n"
|
2015-06-09 01:45:39 +02:00
|
|
|
" p - toggle mini-graph\n"
|
2015-05-30 01:36:40 +02:00
|
|
|
" u - select previous node\n"
|
|
|
|
" V - toggle basicblock / call graphs\n"
|
|
|
|
" x/X - jump to xref/ref\n"
|
|
|
|
" z/Z - step / step over\n"
|
|
|
|
" R - relayout\n");
|
|
|
|
r_cons_flush ();
|
|
|
|
r_cons_any_key (NULL);
|
|
|
|
break;
|
|
|
|
case 'R':
|
2015-06-03 00:18:33 +02:00
|
|
|
case 'r':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_set_layout (g);
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'j':
|
2015-06-03 00:18:33 +02:00
|
|
|
if (r_cons_singleton()->mouse_event) {
|
|
|
|
switch (mousemode) {
|
|
|
|
case 0: // canvas-y
|
|
|
|
can->sy += wheelspeed;
|
|
|
|
break;
|
|
|
|
case 1: // canvas-x
|
|
|
|
can->sx += wheelspeed;
|
|
|
|
break;
|
|
|
|
case 2: // node-y
|
2015-06-12 11:08:05 +02:00
|
|
|
get_anode(g->curnode)->y += wheelspeed;
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
|
|
|
case 3: // node-x
|
2015-06-12 11:08:05 +02:00
|
|
|
get_anode(g->curnode)->x += wheelspeed;
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2015-06-12 11:08:05 +02:00
|
|
|
get_anode(g->curnode)->y++;
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'k':
|
2015-06-03 00:18:33 +02:00
|
|
|
if (r_cons_singleton()->mouse_event) {
|
|
|
|
switch (mousemode) {
|
|
|
|
case 0: // canvas-y
|
|
|
|
can->sy -= wheelspeed;
|
|
|
|
break;
|
|
|
|
case 1: // canvas-x
|
|
|
|
can->sx -= wheelspeed;
|
|
|
|
break;
|
|
|
|
case 2: // node-y
|
2015-06-12 11:08:05 +02:00
|
|
|
get_anode(g->curnode)->y -= wheelspeed;
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
|
|
|
case 3: // node-x
|
2015-06-12 11:08:05 +02:00
|
|
|
get_anode(g->curnode)->x -= wheelspeed;
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2015-06-12 11:08:05 +02:00
|
|
|
get_anode(g->curnode)->y--;
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'm':
|
2015-06-03 00:18:33 +02:00
|
|
|
mousemode++;
|
|
|
|
if (!mousemodes[mousemode])
|
|
|
|
mousemode = 0;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'M':
|
2015-06-03 00:18:33 +02:00
|
|
|
mousemode--;
|
|
|
|
if (mousemode<0)
|
|
|
|
mousemode = 3;
|
|
|
|
break;
|
2015-06-12 11:08:05 +02:00
|
|
|
case 'h': get_anode(g->curnode)->x--; break;
|
|
|
|
case 'l': get_anode(g->curnode)->x++; break;
|
|
|
|
case 'J': get_anode(g->curnode)->y += 5; break;
|
|
|
|
case 'K': get_anode(g->curnode)->y -= 5; break;
|
|
|
|
case 'H': get_anode(g->curnode)->x -= 5; break;
|
|
|
|
case 'L': get_anode(g->curnode)->x += 5; break;
|
2015-05-30 01:36:40 +02:00
|
|
|
// scroll
|
|
|
|
case '0': can->sx = can->sy = 0; break;
|
|
|
|
case 'w': can->sy -= 1; break;
|
|
|
|
case 's': can->sy += 1; break;
|
|
|
|
case 'a': can->sx -= 1; break;
|
|
|
|
case 'd': can->sx += 1; break;
|
|
|
|
case 'W': can->sy -= 5; break;
|
|
|
|
case 'S': can->sy += 5; break;
|
|
|
|
case 'A': can->sx -= 5; break;
|
|
|
|
case 'D': can->sx += 5; break;
|
|
|
|
case 'e':
|
2015-06-03 00:18:33 +02:00
|
|
|
can->linemode = !!!can->linemode;
|
|
|
|
break;
|
2015-06-09 01:45:39 +02:00
|
|
|
case 'p':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_toggle_small_nodes(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'u':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_undo_node(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case '.':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_update_seek (g, get_anode(g->curnode), R_TRUE);
|
2015-06-03 00:18:33 +02:00
|
|
|
g->is_instep = R_TRUE;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 't':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_follow_true(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'f':
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_follow_false(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case '/':
|
2015-06-03 00:18:33 +02:00
|
|
|
r_core_cmd0 (core, "?i highlight;e scr.highlight=`?y`");
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case ':':
|
2015-06-03 00:18:33 +02:00
|
|
|
core->vmode = R_FALSE;
|
|
|
|
r_core_visual_prompt_input (core);
|
|
|
|
core->vmode = R_TRUE;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'C':
|
2015-06-03 00:18:33 +02:00
|
|
|
can->color = !!!can->color;
|
|
|
|
//r_config_swap (core->config, "scr.color");
|
|
|
|
// refresh graph
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case -1: // EOF
|
|
|
|
case 'q':
|
2015-06-03 00:18:33 +02:00
|
|
|
exit_graph = R_TRUE;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 27: // ESC
|
2015-06-03 00:18:33 +02:00
|
|
|
if (r_cons_readchar () == 91) {
|
|
|
|
if (r_cons_readchar () == 90) {
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_prev_node (g);
|
2015-05-30 01:36:40 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
default:
|
2015-06-03 00:18:33 +02:00
|
|
|
eprintf ("Key %d\n", key);
|
|
|
|
//sleep (1);
|
|
|
|
break;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
|
|
|
}
|
2015-05-30 01:36:40 +02:00
|
|
|
|
2015-06-12 11:08:05 +02:00
|
|
|
agraph_free(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
err_graph_new:
|
2015-06-11 03:55:15 +02:00
|
|
|
r_config_set_i (core->config, "scr.color", can->color);
|
2015-06-15 20:44:37 +03:00
|
|
|
free (can);
|
2015-06-03 00:18:33 +02:00
|
|
|
return !is_error;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|