This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] dotgen: Generate Graphiviz format .dot dump of functions cfg


From: Dennis, CHENG Renquan <crquan@fedoraproject.org>

The GCC has default support of dumping gimple cfg in vcg format, but when I
was trying to find a tool to interpret the *.006t.vcg dump file, or to generate
a vector image format, it seemed not easy, the vcgviewer [1] not mature as
Graphviz, and Graph::Easy [2] is a perl CPAN module, but why not add a pass
to generate Graphviz .dot format default? These days it seems Graphviz is more
popular;

[1] http://code.google.com/p/vcgviewer/
[2] http://search.cpan.org/~tels/Graph-Easy/

This is a tentative implementation on dumping ".dot" files directly,

I have tested it with gcc-4.5-20100708 snapshot, it works well [*], however,
a feature of the original vcg not implemented,
1) I don't know how to represent the priority information in a .dot file?

[*] the pass->name is "*cfg2dot", means to translate cfg into a dot file, the
name starting with a star is to conform gcc without dump files, because gcc
default pass dump files implementation would always dump a function name line
with ";; Function ..." that is not understood by Graphviz, so I choose to
implement it with dump_register low-level APIs; however, current gcc doesn't
understand plugin registered passes with name starting with a star, it is
better to apply this patch first;
http://gcc.gnu.org/ml/gcc-patches/2010-07/msg00912.html

Comments are always welcomed, please give ideas on how this could be more
useful?

If asked, I can also transform this into a patch on gcc for official inclusion,
just changed it from a plugin registered pass to gcc internal pass;

+/* 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>
---
 Makefile    |   26 ++++++++
 pass-dgen.c |  206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 232 insertions(+), 0 deletions(-)

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,
+		     &regi_info);
+
+  return 0;
+}

--
Git 1.7.1.1

CHENG Renquan
38 St Thomas Walk, Singapore 238118      http://crquan.fedorapeople.org


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]