diff options
author | Dennis, CHENG Renquan <crquan@fedoraproject.org> | 2010-07-11 23:11:47 +0800 |
---|---|---|
committer | Dennis, CHENG Renquan <crquan@fedoraproject.org> | 2010-07-11 23:11:47 +0800 |
commit | fc85c38529fb8c824f616d4ffaf3d1f04badd0a2 (patch) | |
tree | 1e294551dfbc126e55f73cedcbc6a814ca9e44df | |
download | gcc-dot-gen-plugin-fc85c38529fb8c824f616d4ffaf3d1f04badd0a2.tar.gz gcc-dot-gen-plugin-fc85c38529fb8c824f616d4ffaf3d1f04badd0a2.tar.xz gcc-dot-gen-plugin-fc85c38529fb8c824f616d4ffaf3d1f04badd0a2.zip |
dotgen: Generate Graphiviz format .dot dump of functions control flow graph
+/* 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" <crquan@fedoraproject.org>
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | pass-dgen.c | 206 | ||||
-rw-r--r-- | test1.c | 21 |
4 files changed, 255 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d22eb4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.so diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..453fbeb --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ + +GCC = gcc +CC = gcc + +PLUGIN_FILE = dotgen.so +PLUGIN_SOURCE_FILES = pass-dgen.c +PLUGIN_OBJECT_FILES = $(patsubst %.c,%.o,$(PLUGIN_SOURCE_FILES)) + +GCCPLUGINS_DIR := $(shell $(GCC) -print-file-name=plugin) +CFLAGS += -I$(GCCPLUGINS_DIR)/include -I$(with_gmp)/include -Wall -O2 + +all: $(PLUGIN_FILE) + +$(PLUGIN_FILE): $(PLUGIN_OBJECT_FILES) + $(GCC) -shared -o $@ $^ + +.PHONY: clean all test + +test: + @if [ ! -d dump-files ]; then mkdir dump-files; fi + $(GCC) -fplugin=./$(PLUGIN_FILE) \ + -fdump-tree-all -dumpdir dump-files/ \ + -c test1.c + +clean: + @-rm -f *~ *.o $(PLUGIN_FILE) diff --git a/pass-dgen.c b/pass-dgen.c new file mode 100644 index 0000000..3bf9634 --- /dev/null +++ b/pass-dgen.c @@ -0,0 +1,206 @@ +/* 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" <crquan@fedoraproject.org> + +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 +<http://www.gnu.org/licenses/>. */ + +#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" + +int plugin_is_GPL_compatible; + +static const char *plugin_name; +static struct plugin_info _version_help_info = +{ + "0.1", /* version */ + "Dump internal gimple cfg into Graphviz .dot format", + /* help */ +}; + +static void +gimple_cfg2dot (FILE *file) +{ + 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 (file, "// Function %s\n", funcname); + fprintf (file, "digraph graph_%s {\n\n", funcname); + fprintf (file, " graph [ label=%s, labelloc=top ];\n\n", funcname); + + /* Write blocks and edges. */ + FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs) + { + fprintf (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 (file, "\n"); + } + fputc ('\n', 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 (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 (file, " %d -> EXIT\n", bb->index); + else + fprintf (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 (file, "\n"); */ + } + + if (bb->next_bb != EXIT_BLOCK_PTR) + fputc ('\n', file); + } + + fprintf (file, "}\n\n"); +} + +static unsigned int +execute_pass_cfg2dot () +{ + static int dot_nr; + FILE *dot_file = NULL; + + if (!dot_nr) + { + 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)); + } + } + + if (!dot_file) + dot_file = dump_begin (dot_nr, NULL); + if (dot_file) + { + gimple_cfg2dot (dot_file); + dump_end (dot_nr, dot_file); + } + + return 0; +} + +static struct gimple_opt_pass pass_cfg2dot = +{ + { + GIMPLE_PASS, + "*cfg2dot", /* name */ + NULL, /* 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 */ + } +}; + +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); + + 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; +} @@ -0,0 +1,21 @@ +#include "stdio.h" + +extern int random (); + +int func1 (int a, int b) +{ + return a+b; +} + +int main (void) +{ + int a, b; + + a = random (); + if (a) + a = a + func1 (a, b); + + printf ("result=%d\n", a); + + return 0; +} |