#include "cfg.h"
 #include "main.h"
+#include "xlated_dumper.h"
 
 struct cfg {
        struct list_head funcs;
        }
 }
 
+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;
        if (cfg_build(&cfg, insn, len))
                return;
 
+       cfg_dump(&cfg);
+
        cfg_destroy(&cfg);
 }
 
        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);
                }
        }
 }
+
+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(" | ");
+       }
+}