summaryrefslogtreecommitdiffstats
path: root/tools/bpf/bpftool/cfg.c
diff options
context:
space:
mode:
authorJiong Wang <jiong.wang@netronome.com>2018-03-01 18:01:21 -0800
committerAlexei Starovoitov <ast@kernel.org>2018-03-01 18:29:49 -0800
commitefcef17a6d6575dacca22bce69e139354c5a2170 (patch)
tree2305cd907b66788820d35647f7ef39392550319a /tools/bpf/bpftool/cfg.c
parent6e9eb7e3f8705e5e3f1a6a2b47450ec606ab012b (diff)
downloadlinux-efcef17a6d6575dacca22bce69e139354c5a2170.tar.gz
linux-efcef17a6d6575dacca22bce69e139354c5a2170.tar.bz2
linux-efcef17a6d6575dacca22bce69e139354c5a2170.zip
tools: bpftool: generate .dot graph from CFG information
This patch let bpftool print .dot graph file into stdout. This graph is generated by the following steps: - iterate through the function list. - generate basic-block(BB) definition for each BB in the function. - draw out edges to connect BBs. This patch is the initial support, the layout and decoration of the .dot graph could be improved. Also, it will be useful if we could visualize some performance data from static analysis. Signed-off-by: Jiong Wang <jiong.wang@netronome.com> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools/bpf/bpftool/cfg.c')
-rw-r--r--tools/bpf/bpftool/cfg.c93
1 files changed, 93 insertions, 0 deletions
diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
index c57f88cf2834..f30b3a4a840b 100644
--- a/tools/bpf/bpftool/cfg.c
+++ b/tools/bpf/bpftool/cfg.c
@@ -41,6 +41,7 @@
#include "cfg.h"
#include "main.h"
+#include "xlated_dumper.h"
struct cfg {
struct list_head funcs;
@@ -408,6 +409,96 @@ static void cfg_destroy(struct cfg *cfg)
}
}
+static void draw_bb_node(struct func_node *func, struct bb_node *bb)
+{
+ const char *shape;
+
+ if (bb->idx == ENTRY_BLOCK_INDEX || bb->idx == EXIT_BLOCK_INDEX)
+ shape = "Mdiamond";
+ else
+ shape = "record";
+
+ printf("\tfn_%d_bb_%d [shape=%s,style=filled,label=\"",
+ func->idx, bb->idx, shape);
+
+ if (bb->idx == ENTRY_BLOCK_INDEX) {
+ printf("ENTRY");
+ } else if (bb->idx == EXIT_BLOCK_INDEX) {
+ printf("EXIT");
+ } else {
+ unsigned int start_idx;
+ struct dump_data dd = {};
+
+ printf("{");
+ kernel_syms_load(&dd);
+ start_idx = bb->head - func->start;
+ dump_xlated_for_graph(&dd, bb->head, bb->tail, start_idx);
+ kernel_syms_destroy(&dd);
+ printf("}");
+ }
+
+ printf("\"];\n\n");
+}
+
+static void draw_bb_succ_edges(struct func_node *func, struct bb_node *bb)
+{
+ const char *style = "\"solid,bold\"";
+ const char *color = "black";
+ int func_idx = func->idx;
+ struct edge_node *e;
+ int weight = 10;
+
+ if (list_empty(&bb->e_succs))
+ return;
+
+ list_for_each_entry(e, &bb->e_succs, l) {
+ printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=%s, color=%s, weight=%d, constraint=true",
+ func_idx, e->src->idx, func_idx, e->dst->idx,
+ style, color, weight);
+ printf("];\n");
+ }
+}
+
+static void func_output_bb_def(struct func_node *func)
+{
+ struct bb_node *bb;
+
+ list_for_each_entry(bb, &func->bbs, l) {
+ draw_bb_node(func, bb);
+ }
+}
+
+static void func_output_edges(struct func_node *func)
+{
+ int func_idx = func->idx;
+ struct bb_node *bb;
+
+ list_for_each_entry(bb, &func->bbs, l) {
+ draw_bb_succ_edges(func, bb);
+ }
+
+ /* Add an invisible edge from ENTRY to EXIT, this is to
+ * improve the graph layout.
+ */
+ printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=\"invis\", constraint=true];\n",
+ func_idx, ENTRY_BLOCK_INDEX, func_idx, EXIT_BLOCK_INDEX);
+}
+
+static void cfg_dump(struct cfg *cfg)
+{
+ struct func_node *func;
+
+ printf("digraph \"DOT graph for eBPF program\" {\n");
+ list_for_each_entry(func, &cfg->funcs, l) {
+ printf("subgraph \"cluster_%d\" {\n\tstyle=\"dashed\";\n\tcolor=\"black\";\n\tlabel=\"func_%d ()\";\n",
+ func->idx, func->idx);
+ func_output_bb_def(func);
+ func_output_edges(func);
+ printf("}\n");
+ }
+ printf("}\n");
+}
+
void dump_xlated_cfg(void *buf, unsigned int len)
{
struct bpf_insn *insn = buf;
@@ -417,5 +508,7 @@ void dump_xlated_cfg(void *buf, unsigned int len)
if (cfg_build(&cfg, insn, len))
return;
+ cfg_dump(&cfg);
+
cfg_destroy(&cfg);
}