diff options
-rw-r--r-- | tools/bpf/bpftool/cfg.c | 93 | ||||
-rw-r--r-- | tools/bpf/bpftool/xlated_dumper.c | 52 | ||||
-rw-r--r-- | tools/bpf/bpftool/xlated_dumper.h | 2 |
3 files changed, 147 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); } diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c index dfcdf794c9d1..20da835e9e38 100644 --- a/tools/bpf/bpftool/xlated_dumper.c +++ b/tools/bpf/bpftool/xlated_dumper.c @@ -123,6 +123,37 @@ static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) va_end(args); } +static void +print_insn_for_graph(struct bpf_verifier_env *env, const char *fmt, ...) +{ + char buf[64], *p; + va_list args; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + p = buf; + while (*p != '\0') { + if (*p == '\n') { + memmove(p + 3, p, strlen(buf) + 1 - (p - buf)); + /* Align each instruction dump row left. */ + *p++ = '\\'; + *p++ = 'l'; + /* Output multiline concatenation. */ + *p++ = '\\'; + } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') { + memmove(p + 1, p, strlen(buf) + 1 - (p - buf)); + /* Escape special character. */ + *p++ = '\\'; + } + + p++; + } + + printf("%s", buf); +} + static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) { unsigned int l = strlen(fmt); @@ -284,3 +315,24 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, } } } + +void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end, + unsigned int start_idx) +{ + const struct bpf_insn_cbs cbs = { + .cb_print = print_insn_for_graph, + .cb_call = print_call, + .cb_imm = print_imm, + .private_data = dd, + }; + struct bpf_insn *insn_start = buf_start; + struct bpf_insn *insn_end = buf_end; + struct bpf_insn *cur = insn_start; + + for (; cur <= insn_end; cur++) { + printf("% 4d: ", (int)(cur - insn_start + start_idx)); + print_bpf_insn(&cbs, NULL, cur, true); + if (cur != insn_end) + printf(" | "); + } +} diff --git a/tools/bpf/bpftool/xlated_dumper.h b/tools/bpf/bpftool/xlated_dumper.h index 208285f9ab70..51c935d38ae2 100644 --- a/tools/bpf/bpftool/xlated_dumper.h +++ b/tools/bpf/bpftool/xlated_dumper.h @@ -58,5 +58,7 @@ void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, bool opcodes); void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, bool opcodes); +void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end, + unsigned int start_index); #endif |