/* Generate Graphiviz format .dot dump of functions control flow graph, similar as the gcc internal vcg dump. Copyright (C) 2010 by Author: "Dennis, CHENG Renquan" This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "gcc-plugin.h" #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "tree.h" #include "rtl.h" #include "basic-block.h" #include "function.h" #include "langhooks.h" #include "tree-flow.h" #include "tree-dump.h" #include "tree-pass.h" #include "toplev.h" int plugin_is_GPL_compatible; /* global variables */ static const char *plugin_name; static int dot_nr; static FILE *dot_file = NULL; static bool disabled = false; static struct plugin_info _version_help_info = { "0.1", /* version */ "Dump internal gimple cfg into Graphviz .dot format", /* help */ }; static void gimple_cfg2dot (void) { edge e; edge_iterator ei; basic_block bb; const char *funcname = lang_hooks.decl_printable_name (current_function_decl, 2); /* Write the file header. */ fprintf (dot_file, "// Function %s\n", funcname); fprintf (dot_file, "digraph graph_%s {\n\n", funcname); fprintf (dot_file, " graph [ label=%s, labelloc=top ];\n\n", funcname); /* Write blocks and edges. */ FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs) { fprintf (dot_file, " ENTRY -> %d", e->dest->index); /* how to represent this in Graphviz dot ??? */ /* if (e->flags & EDGE_FAKE) */ /* fprintf (file, " linestyle: dotted priority: 10"); */ /* else */ /* fprintf (file, " linestyle: solid priority: 100"); */ fprintf (dot_file, "\n"); } fputc ('\n', dot_file); FOR_EACH_BB (bb) { enum gimple_code head_code, end_code; const char *head_name, *end_name; int head_line = 0; int end_line = 0; gimple first = first_stmt (bb); gimple last = last_stmt (bb); if (first) { head_code = gimple_code (first); head_name = gimple_code_name[head_code]; head_line = get_lineno (first); } else head_name = "no-statement"; if (last) { end_code = gimple_code (last); end_name = gimple_code_name[end_code]; end_line = get_lineno (last); } else end_name = "no-statement"; fprintf (dot_file, " %d [ label=\"#%d\\n%s (%d)\\n%s (%d)\" ]\n", bb->index, bb->index, head_name, head_line, end_name, end_line); FOR_EACH_EDGE (e, ei, bb->succs) { if (e->dest == EXIT_BLOCK_PTR) fprintf (dot_file, " %d -> EXIT\n", bb->index); else { fprintf (dot_file, " %d -> %d", bb->index, e->dest->index); /* if (e->flags & EDGE_FAKE) */ /* fprintf (file, " priority: 10 linestyle: dotted"); */ /* else */ /* fprintf (file, " priority: 100 linestyle: solid"); */ fprintf (dot_file, "\n"); } } if (bb->next_bb != EXIT_BLOCK_PTR) fputc ('\n', dot_file); } fprintf (dot_file, "}\n\n"); } static void open_dump_file (void) { dot_nr = dump_register (".dot", NULL, NULL, TDF_TREE); /* manually set dfi->state to -1 enable it, because the dump_enable is static, cannot be used by plugins. */ get_dump_file_info (dot_nr)->state = -1; dot_file = dump_begin (dot_nr, NULL); if (dot_file) { time_t now; time (&now); fprintf (dot_file, "\n// Generated by %s gcc plugin %s at %s\n", plugin_name, _version_help_info.version, ctime (&now)); } else { warning (0, "dot dump file open failed, diable this pass for following functions."); disabled = true; } } static bool gate_enabled (void) { return !disabled; } static unsigned int execute_pass_cfg2dot (void) { if (!dot_file) open_dump_file (); gimple_cfg2dot (); return 0; } static struct gimple_opt_pass pass_cfg2dot = { { GIMPLE_PASS, "*cfg2dot", /* name */ gate_enabled, /* gate */ execute_pass_cfg2dot, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ TV_NONE, /* tv_id */ PROP_cfg, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ 0 /* todo_flags_finish */ } }; static void plugin_finish_unit (void *gcc_data, void *user_data) { if (dot_file) { dump_end (dot_nr, dot_file); dot_file = NULL; dot_nr = 0; } } int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) { struct register_pass_info regi_info; plugin_name = plugin_info->base_name; register_callback (plugin_name, PLUGIN_INFO, NULL, &_version_help_info); register_callback (plugin_name, PLUGIN_FINISH_UNIT, plugin_finish_unit, NULL); regi_info.pass = &pass_cfg2dot.pass; regi_info.reference_pass_name = "cfg"; regi_info.ref_pass_instance_number = 0; regi_info.pos_op = PASS_POS_INSERT_AFTER; register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, ®i_info); return 0; }