commit 6c63c68ba85134c003977b5e4e8cd4347abbf859
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Jul 27 19:39:24 2011 -0400
Wrap GCC's callgraph information, exposing it via a Python API
Introduce:
gcc.CallgraphNode
gcc.CallgraphEdge
gcc.get_callgraph_nodes()
gccutils.callgraph_to_dot
New sample script:
examples/show-callgraph.py
Document the above
Add selftest
Makefile | 5 +
docs/callgraph.rst | 71 +++++++++++++++
docs/examples.rst | 21 +++++
docs/misc.rst | 1 +
docs/sample-callgraph.png | Bin 0 -> 6441 bytes
docs/tree.rst | 5 +
examples/show-callgraph.py | 29 ++++++
gcc-python-callgraph.c | 171 +++++++++++++++++++++++++++++++++++++
gcc-python-wrappers.h | 25 ++++++
gcc-python.c | 41 +++++++++
gcc-python.h | 14 +++
gccutils.py | 31 +++++++
generate-callgraph-c.py | 130 ++++++++++++++++++++++++++++
generate-tree-c.py | 3 +
tests/plugin/callgraph/input.c | 47 ++++++++++
tests/plugin/callgraph/script.py | 48 ++++++++++
tests/plugin/callgraph/stdout.txt | 94 ++++++++++++++++++++
17 files changed, 736 insertions(+), 0 deletions(-)
---
diff --git a/Makefile b/Makefile
index c378595..105c64e 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@ GCC=gcc
PLUGIN_SOURCE_FILES= \
gcc-python.c \
+ gcc-python-callgraph.c \
gcc-python-cfg.c \
gcc-python-closure.c \
gcc-python-function.c \
@@ -30,6 +31,7 @@ PLUGIN_SOURCE_FILES= \
gcc-python-pretty-printer.c \
gcc-python-tree.c \
gcc-python-variable.c \
+ autogenerated-callgraph.c \
autogenerated-cfg.c \
autogenerated-option.c \
autogenerated-function.c \
@@ -105,6 +107,9 @@ autogenerated-gimple-types.txt: gimple-types.txt.in
autogenerated-tree-types.txt: tree-types.txt.in
cpp $(CFLAGS) $^ -o $@
+autogenerated-callgraph.c: cpybuilder.py generate-callgraph-c.py
+ python generate-callgraph-c.py > $@
+
autogenerated-cfg.c: cpybuilder.py generate-cfg-c.py
$(PYTHON) generate-cfg-c.py > $@
diff --git a/docs/callgraph.rst b/docs/callgraph.rst
new file mode 100644
index 0000000..13d1970
--- /dev/null
+++ b/docs/callgraph.rst
@@ -0,0 +1,71 @@
+.. Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This 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 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program. If not, see
+ <
http://www.gnu.org/licenses/>.
+
+Interprocedural analysis (IPA)
+==============================
+GCC builds a "call graph", recording which functions call which other
+functions, and it uses this for various optimizations.
+
+It is constructed by the `"*build_cgraph_edges"` pass.
+
+In case it's of interest, it is available via the following Python API:
+
+.. py:function:: gcc.get_callgraph_nodes()
+
+ Get a list of all :py:class:`gcc.CallgraphNode` instances
+
+.. py:function:: gccutils.callgraph_to_dot()
+
+ Return the GraphViz source for a rendering of the current callgraph, as a
+ string.
+
+ Here's an example of such a rendering:
+
+ .. figure:: sample-callgraph.png
+ :alt: image of a call graph
+
+.. py:class:: gcc.CallgraphNode
+
+ .. py:attribute:: decl
+
+ The :py:class:`gcc.FunctionDecl` for this node within the callgraph
+
+ .. py:attribute:: callees
+
+ The function calls made by this function, as a list of
:py:class:`gcc.CallgraphEdge` instances
+
+ .. py:attribute:: callers
+
+ The places that call this function, as a list of :py:class:`gcc.CallgraphEdge`
instances
+
+ Internally, this wraps a `struct cgraph_node *`
+
+.. py:class:: gcc.CallgraphEdge
+
+ .. py:attribute:: caller
+
+ The function that makes this call, as a :py:class:`gcc.CallgraphNode`
+
+ .. py:attribute:: callee
+
+ The function that is called here, as a :py:class:`gcc.CallgraphNode`
+
+ .. py:attribute:: call_stmt
+
+ The :py:class:`gcc.GimpleCall` statememt for the function call
+
+ Internally, this wraps a `struct cgraph_edge *`
diff --git a/docs/examples.rst b/docs/examples.rst
index cb448cd..00ddeac 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -139,3 +139,24 @@ The Python code for this is:
.. literalinclude:: ../examples/show-ssa.py
:lines: 17-
:language: python
+
+`show-callgraph.py`
+-------------------
+
+This simple script sends GCC's interprocedural analysis data through GraphViz.
+
+ .. code-block:: bash
+
+ $ ./gcc-with-python examples/show-callgraph.py test.c
+
+It generates an image file showing which functions call which other functions,
+and opens it in a viewer.
+
+.. figure:: sample-callgraph.png
+ :alt: image of a call graph
+
+The Python code for this is:
+
+ .. literalinclude:: ../examples/show-callgraph.py
+ :lines: 18-
+ :language: python
diff --git a/docs/misc.rst b/docs/misc.rst
index ec1f831..0c707e1 100644
--- a/docs/misc.rst
+++ b/docs/misc.rst
@@ -24,6 +24,7 @@ scripts.
.. toctree::
+ callgraph.rst
options.rst
parameters.rst
versions.rst
diff --git a/docs/sample-callgraph.png b/docs/sample-callgraph.png
new file mode 100644
index 0000000..7437458
Binary files /dev/null and b/docs/sample-callgraph.png differ
diff --git a/docs/tree.rst b/docs/tree.rst
index 566a605..0ef2912 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -217,6 +217,11 @@ Declarations
The :py:class:`gcc.ResultDecl` representing the return value of this
function
+ .. py:attribute:: callgraph_node
+
+ The :py:class:`gcc.CallgraphNode` for this function declaration, or
+ `None`
+
.. Declaration
.. ClassMethodDecl
.. ConstDecl
diff --git a/examples/show-callgraph.py b/examples/show-callgraph.py
new file mode 100644
index 0000000..54be076
--- /dev/null
+++ b/examples/show-callgraph.py
@@ -0,0 +1,29 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 Red Hat, Inc.
+#
+# This 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see
+# <
http://www.gnu.org/licenses/>.
+
+# Sample python script, to be run by our gcc plugin
+# Show the call graph (interprocedural analysis), using GraphViz
+import gcc
+from gccutils import callgraph_to_dot, invoke_dot
+
+def on_pass_execution(p, fn):
+ if p.name == '*free_lang_data':
+ dot = callgraph_to_dot()
+ invoke_dot(dot)
+
+gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ on_pass_execution)
diff --git a/gcc-python-callgraph.c b/gcc-python-callgraph.c
new file mode 100644
index 0000000..4aad047
--- /dev/null
+++ b/gcc-python-callgraph.c
@@ -0,0 +1,171 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This 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 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program. If not, see
+ <
http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include "gcc-python.h"
+#include "gcc-python-wrappers.h"
+
+/*
+ Wrapper for various types in gcc/cgraph.h
+ struct cgraph_edge *
+ struct cgraph_node *
+*/
+
+PyObject *
+gcc_CallgraphEdge_repr(struct PyGccCallgraphEdge * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphEdge_str(struct PyGccCallgraphEdge * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphNode_repr(struct PyGccCallgraphNode * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphNode_str(struct PyGccCallgraphNode * self)
+{
+ return gcc_python_string_from_format("%s()",
+ Py_TYPE(self)->tp_name);
+}
+
+PyObject *
+gcc_CallgraphNode_get_callees(struct PyGccCallgraphNode * self)
+{
+ PyObject *result;
+ struct cgraph_edge *edge;
+
+ result = PyList_New(0);
+ if (!result) {
+ goto error;
+ }
+
+ for (edge = self->node->callees; edge ; edge = edge->next_callee) {
+ PyObject *obj_var = gcc_python_make_wrapper_cgraph_edge(edge);
+ if (!obj_var) {
+ goto error;
+ }
+ if (-1 == PyList_Append(result, obj_var)) {
+ Py_DECREF(obj_var);
+ goto error;
+ }
+ }
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ return NULL;
+}
+
+PyObject *
+gcc_CallgraphNode_get_callers(struct PyGccCallgraphNode * self)
+{
+ PyObject *result;
+ struct cgraph_edge *edge;
+
+ result = PyList_New(0);
+ if (!result) {
+ goto error;
+ }
+
+ for (edge = self->node->callers; edge ; edge = edge->next_caller) {
+ PyObject *obj_var = gcc_python_make_wrapper_cgraph_edge(edge);
+ if (!obj_var) {
+ goto error;
+ }
+ if (-1 == PyList_Append(result, obj_var)) {
+ Py_DECREF(obj_var);
+ goto error;
+ }
+ }
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ return NULL;
+}
+
+PyObject *
+gcc_python_make_wrapper_cgraph_edge(struct cgraph_edge * edge)
+{
+ struct PyGccCallgraphEdge *obj = NULL;
+
+ obj = PyObject_New(struct PyGccCallgraphEdge, &gcc_CallgraphEdgeType);
+ if (!obj) {
+ goto error;
+ }
+
+ obj->edge = edge;
+ /* FIXME: do we need to do something for the GCC GC? */
+
+ return (PyObject*)obj;
+
+error:
+ return NULL;
+}
+
+PyObject *
+real_make_cgraph_node_wrapper(void *ptr)
+{
+ struct cgraph_node * node = (struct cgraph_node *)ptr;
+ struct PyGccCallgraphNode *obj = NULL;
+
+ obj = PyObject_New(struct PyGccCallgraphNode, &gcc_CallgraphNodeType);
+ if (!obj) {
+ goto error;
+ }
+
+ obj->node = node;
+ /* FIXME: do we need to do something for the GCC GC? */
+
+ return (PyObject*)obj;
+
+error:
+ return NULL;
+}
+
+static PyObject *cgraph_node_wrapper_cache = NULL;
+PyObject *
+gcc_python_make_wrapper_cgraph_node(struct cgraph_node * node)
+{
+ return gcc_python_lazily_create_wrapper(&cgraph_node_wrapper_cache,
+ node,
+ real_make_cgraph_node_wrapper);
+}
+
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index 2a82d88..fd4be65 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -23,12 +23,34 @@
#include "gcc-python.h"
#include "tree-pass.h"
#include "opts.h"
+#include "cgraph.h"
PyMODINIT_FUNC initoptpass(void);
+/* gcc-python-callgraph.c: */
+PyObject *
+gcc_CallgraphEdge_repr(struct PyGccCallgraphEdge * self);
+
+PyObject *
+gcc_CallgraphEdge_str(struct PyGccCallgraphEdge * self);
+
+PyObject *
+gcc_CallgraphNode_repr(struct PyGccCallgraphNode * self);
+
+PyObject *
+gcc_CallgraphNode_str(struct PyGccCallgraphNode * self);
+
+extern PyObject *
+gcc_CallgraphNode_get_callers(struct PyGccCallgraphNode * self);
+
+extern PyObject *
+gcc_CallgraphNode_get_callees(struct PyGccCallgraphNode * self);
+
+/* gcc-python-pass.c: */
extern PyObject *
gcc_python_make_wrapper_pass(struct opt_pass *pass);
+/* gcc-python-location.c: */
PyObject *
gcc_Location_repr(struct PyGccLocation * self);
@@ -38,6 +60,7 @@ gcc_Location_str(struct PyGccLocation * self);
PyObject *
gcc_Location_richcompare(PyObject *o1, PyObject *o2, int op);
+/* gcc-python-cfg.c: */
PyObject *
gcc_BasicBlock_get_preds(PyGccBasicBlock *self, void *closure);
@@ -53,6 +76,7 @@ gcc_BasicBlock_get_phi_nodes(PyGccBasicBlock *self, void *closure);
PyObject *
gcc_Cfg_get_basic_blocks(PyGccCfg *self, void *closure);
+/* gcc-python-tree.c: */
PyObject *
gcc_Tree_str(struct PyGccTree * self);
@@ -86,6 +110,7 @@ gcc_IntegerConstant_get_constant(struct PyGccTree * self, void
*closure);
PyObject *
gcc_TypeDecl_get_pointer(struct PyGccTree *self, void *closure);
+/* gcc-python-gimple.c: */
PyObject *
gcc_Gimple_repr(struct PyGccGimple * self);
diff --git a/gcc-python.c b/gcc-python.c
index 850c62d..aebcd5a 100644
--- a/gcc-python.c
+++ b/gcc-python.c
@@ -605,6 +605,42 @@ gcc_python_get_gcc_version(PyObject *self, PyObject *args)
return gcc_version_to_object(actual_gcc_version);
}
+static PyObject *
+gcc_python_get_callgraph_nodes(PyObject *self, PyObject *args)
+{
+ PyObject *result;
+ struct cgraph_node *node;
+
+ /* For debugging, see GCC's dump of things: */
+ if (0) {
+ fprintf(stderr, "----------------BEGIN----------------\n");
+ dump_cgraph (stderr);
+ fprintf(stderr, "---------------- END ----------------\n");
+ }
+
+ result = PyList_New(0);
+ if (!result) {
+ goto error;
+ }
+
+ for (node = cgraph_nodes; node; node = node->next) {
+ PyObject *obj_var = gcc_python_make_wrapper_cgraph_node(node);
+ if (!obj_var) {
+ goto error;
+ }
+ if (-1 == PyList_Append(result, obj_var)) {
+ Py_DECREF(obj_var);
+ goto error;
+ }
+ }
+
+ return result;
+
+ error:
+ Py_XDECREF(result);
+ return NULL;
+}
+
static PyMethodDef GccMethods[] = {
{"register_callback",
(PyCFunction)gcc_python_register_callback,
@@ -662,6 +698,9 @@ static PyMethodDef GccMethods[] = {
{"get_gcc_version", gcc_python_get_gcc_version, METH_VARARGS,
"Get the gcc.Version for this version of GCC"},
+ {"get_callgraph_nodes", gcc_python_get_callgraph_nodes, METH_VARARGS,
+ "Get a list of all gcc.CallgraphNode instances"},
+
/* Sentinel: */
{NULL, NULL, 0, NULL}
};
@@ -930,6 +969,7 @@ plugin_init (struct plugin_name_args *plugin_info,
PyStructSequence_InitType(&GccVersionType, &gcc_version_desc);
+ autogenerated_callgraph_init_types(); /* FIXME: error checking! */
autogenerated_cfg_init_types(); /* FIXME: error checking! */
autogenerated_function_init_types(); /* FIXME: error checking! */
autogenerated_gimple_init_types(); /* FIXME: error checking! */
@@ -943,6 +983,7 @@ plugin_init (struct plugin_name_args *plugin_info,
+ autogenerated_callgraph_add_types(gcc_python_globals.module);
autogenerated_cfg_add_types(gcc_python_globals.module);
autogenerated_function_add_types(gcc_python_globals.module);
autogenerated_gimple_add_types(gcc_python_globals.module);
diff --git a/gcc-python.h b/gcc-python.h
index 4e88b71..c2d0131 100644
--- a/gcc-python.h
+++ b/gcc-python.h
@@ -61,6 +61,16 @@
\
/* end of macro */
+DECLARE_SIMPLE_WRAPPER(PyGccCallgraphEdge,
+ gcc_CallgraphEdgeType,
+ cgraph_edge,
+ struct cgraph_edge *, edge)
+
+DECLARE_SIMPLE_WRAPPER(PyGccCallgraphNode,
+ gcc_CallgraphNodeType,
+ cgraph_node,
+ struct cgraph_node *, node)
+
DECLARE_SIMPLE_WRAPPER(PyGccPass,
gcc_PassType,
pass,
@@ -115,6 +125,10 @@ DECLARE_SIMPLE_WRAPPER(PyGccVariable,
variable,
struct varpool_node *, var)
+/* autogenerated-callgraph.c */
+int autogenerated_callgraph_init_types(void);
+void autogenerated_callgraph_add_types(PyObject *m);
+
/* autogenerated-cfg.c */
int autogenerated_cfg_init_types(void);
void autogenerated_cfg_add_types(PyObject *m);
diff --git a/gccutils.py b/gccutils.py
index 45924fe..fbed897 100644
--- a/gccutils.py
+++ b/gccutils.py
@@ -553,3 +553,34 @@ class Table(object):
out.write(self._colsep)
out.write(self._sepchar * width)
out.write('\n')
+
+class CallgraphPrettyPrinter(DotPrettyPrinter):
+ def node_id(self, cgn):
+ return 'cgn%i' % id(cgn)
+
+ def node_to_dot_label(self, cgn):
+ return str(cgn.decl.name)
+
+ def edge_to_dot(self, e):
+ attrliststr = ''
+ return (' %s -> %s %s;\n'
+ % (self.node_id(e.caller),
+ self.node_id(e.callee),
+ attrliststr))
+
+ def to_dot(self):
+ result = 'digraph Callgraph {\n'
+ #result += ' subgraph cluster_callgraph {\n'
+ result += ' node [shape=box];\n'
+ for cgn in gcc.get_callgraph_nodes():
+ result += (' %s [label=<%s>];\n'
+ % (self.node_id(cgn), self.node_to_dot_label(cgn)))
+ for edge in cgn.callers:
+ result += self.edge_to_dot(edge)
+ #result += ' }\n'
+ result += '}\n'
+ return result
+
+def callgraph_to_dot():
+ pp = CallgraphPrettyPrinter()
+ return pp.to_dot()
diff --git a/generate-callgraph-c.py b/generate-callgraph-c.py
new file mode 100644
index 0000000..c2beb28
--- /dev/null
+++ b/generate-callgraph-c.py
@@ -0,0 +1,130 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 Red Hat, Inc.
+#
+# This 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see
+# <
http://www.gnu.org/licenses/>.
+
+from cpybuilder import *
+
+cu = CompilationUnit()
+cu.add_include('gcc-python.h')
+cu.add_include('gcc-python-wrappers.h')
+cu.add_include('gcc-plugin.h')
+cu.add_include("cgraph.h")
+
+modinit_preinit = ''
+modinit_postinit = ''
+
+def generate_callgraph_edge():
+ #
+ # Generate the gcc.CallgraphEdge class:
+ #
+ global modinit_preinit
+ global modinit_postinit
+
+ getsettable = PyGetSetDefTable('gcc_CallgraphEdge_getset_table', [],
+ identifier_prefix='gcc_CallgraphEdge',
+ typename='PyGccCallgraphEdge')
+ getsettable.add_simple_getter(cu,
+ 'caller',
+
'gcc_python_make_wrapper_cgraph_node(self->edge->caller)',
+ 'The function that makes this call, as a
gcc.CallgraphNode')
+ getsettable.add_simple_getter(cu,
+ 'callee',
+
'gcc_python_make_wrapper_cgraph_node(self->edge->callee)',
+ 'The function that is called here, as a
gcc.CallgraphNode')
+ getsettable.add_simple_getter(cu,
+ 'call_stmt',
+
'gcc_python_make_wrapper_gimple(self->edge->call_stmt)',
+ 'The gcc.GimpleCall statememt for the function
call')
+ cu.add_defn(getsettable.c_defn())
+
+ pytype = PyTypeObject(identifier = 'gcc_CallgraphEdgeType',
+ localname = 'CallgraphEdge',
+ tp_name = 'gcc.CallgraphEdge',
+ struct_name = 'struct PyGccCallgraphEdge',
+ tp_new = 'PyType_GenericNew',
+ tp_getset = getsettable.identifier,
+ tp_repr = '(reprfunc)gcc_CallgraphEdge_repr',
+ tp_str = '(reprfunc)gcc_CallgraphEdge_str',
+ )
+ cu.add_defn(pytype.c_defn())
+ modinit_preinit += pytype.c_invoke_type_ready()
+ modinit_postinit += pytype.c_invoke_add_to_module()
+
+generate_callgraph_edge()
+
+def generate_callgraph_node():
+ #
+ # Generate the gcc.CallgraphNode class:
+ #
+ global modinit_preinit
+ global modinit_postinit
+
+ getsettable = PyGetSetDefTable('gcc_CallgraphNode_getset_table', [],
+ identifier_prefix='gcc_CallgraphNode',
+ typename='PyGccCallgraphNode')
+ # FIXME: add getters
+ getsettable.add_simple_getter(cu,
+ 'decl',
+
'gcc_python_make_wrapper_tree(self->node->decl)',
+ 'The gcc.FunctionDecl for this node')
+ getsettable.add_gsdef('callees',
+ 'gcc_CallgraphNode_get_callees',
+ None,
+ 'The function calls made by this function, as a list of
gcc.CallgraphEdge')
+ getsettable.add_gsdef('callers',
+ 'gcc_CallgraphNode_get_callers',
+ None,
+ 'The places that call this function, as a list of
gcc.CallgraphEdge')
+ cu.add_defn(getsettable.c_defn())
+
+ # see gcc/cgraph.c: dump_cgraph_node (FILE *f, struct cgraph_node *node)
+
+ pytype = PyTypeObject(identifier = 'gcc_CallgraphNodeType',
+ localname = 'CallgraphNode',
+ tp_name = 'gcc.CallgraphNode',
+ struct_name = 'struct PyGccCallgraphNode',
+ tp_new = 'PyType_GenericNew',
+ tp_getset = getsettable.identifier,
+ tp_repr = '(reprfunc)gcc_CallgraphNode_repr',
+ tp_str = '(reprfunc)gcc_CallgraphNode_str',
+ )
+ cu.add_defn(pytype.c_defn())
+ modinit_preinit += pytype.c_invoke_type_ready()
+ modinit_postinit += pytype.c_invoke_add_to_module()
+
+generate_callgraph_node()
+
+cu.add_defn("""
+int autogenerated_callgraph_init_types(void)
+{
+""" + modinit_preinit + """
+ return 1;
+
+error:
+ return 0;
+}
+""")
+
+cu.add_defn("""
+void autogenerated_callgraph_add_types(PyObject *m)
+{
+""" + modinit_postinit + """
+}
+""")
+
+
+
+print(cu.as_str())
diff --git a/generate-tree-c.py b/generate-tree-c.py
index 70fd7d3..dbc6d73 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -445,6 +445,9 @@ def generate_tree_code_classes():
add_simple_getter('result',
'gcc_python_make_wrapper_tree(DECL_RESULT_FLD(self->t))',
'The gcc.ResultDecl for the return value')
+ add_simple_getter('callgraph_node',
+
'gcc_python_make_wrapper_cgraph_node(cgraph_get_node(self->t))',
+ 'The gcc.CallgraphNode for this function declaration,
or None')
if tree_type.SYM == 'SSA_NAME':
# c.f. "struct GTY(()) tree_ssa_name":
diff --git a/tests/plugin/callgraph/input.c b/tests/plugin/callgraph/input.c
new file mode 100644
index 0000000..0794207
--- /dev/null
+++ b/tests/plugin/callgraph/input.c
@@ -0,0 +1,47 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This 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 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program. If not, see
+ <
http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+
+void foo()
+{
+}
+
+void bar(void)
+{
+ foo();
+ foo();
+ foo();
+}
+
+void baz(void)
+{
+ bar();
+ bar();
+ bar();
+}
+
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/plugin/callgraph/script.py b/tests/plugin/callgraph/script.py
new file mode 100644
index 0000000..46a02d9
--- /dev/null
+++ b/tests/plugin/callgraph/script.py
@@ -0,0 +1,48 @@
+# Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+# Copyright 2011 Red Hat, Inc.
+#
+# This 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see
+# <
http://www.gnu.org/licenses/>.
+
+# Verify examining details of functions
+
+import gcc
+from gccutils import pprint
+
+help(gcc.CallgraphNode)
+help(gcc.CallgraphEdge)
+
+from gccutils import callgraph_to_dot, invoke_dot
+
+def on_pass_execution(p, fn):
+ #if p.name == '*warn_function_return':
+ if p.name == '*free_lang_data':
+ if 0:
+ dot = callgraph_to_dot()
+ invoke_dot(dot)
+
+ for cgn in gcc.get_callgraph_nodes():
+ print('cgn:')
+ # print(dir(cgn))
+ print(' cgn.decl: %r' % cgn.decl)
+ print(' cgn.callers: %r' % cgn.callers)
+ print(' cgn.callees: %r' % cgn.callees)
+ for e in cgn.callers:
+ print(e)
+ print('e.caller: %r' % e.caller)
+ print('e.callee: %r' % e.callee)
+ print('e.call_stmt: %r %s' % (e.call_stmt, e.call_stmt))
+
+gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
+ on_pass_execution)
diff --git a/tests/plugin/callgraph/stdout.txt b/tests/plugin/callgraph/stdout.txt
new file mode 100644
index 0000000..f9560e3
--- /dev/null
+++ b/tests/plugin/callgraph/stdout.txt
@@ -0,0 +1,94 @@
+Help on class CallgraphNode in module gcc:
+
+class CallgraphNode(__builtin__.object)
+ | Methods defined here:
+ |
+ | __repr__(...)
+ | x.__repr__() <==> repr(x)
+ |
+ | __str__(...)
+ | x.__str__() <==> str(x)
+ |
+ | ----------------------------------------------------------------------
+ | Data descriptors defined here:
+ |
+ | callees
+ | The function calls made by this function, as a list of gcc.CallgraphEdge
+ |
+ | callers
+ | The places that call this function, as a list of gcc.CallgraphEdge
+ |
+ | decl
+ | The gcc.FunctionDecl for this node
+ |
+ | ----------------------------------------------------------------------
+ | Data and other attributes defined here:
+ |
+ | __new__ = <built-in method __new__ of type object>
+ | T.__new__(S, ...) -> a new object with type S, a subtype of T
+
+Help on class CallgraphEdge in module gcc:
+
+class CallgraphEdge(__builtin__.object)
+ | Methods defined here:
+ |
+ | __repr__(...)
+ | x.__repr__() <==> repr(x)
+ |
+ | __str__(...)
+ | x.__str__() <==> str(x)
+ |
+ | ----------------------------------------------------------------------
+ | Data descriptors defined here:
+ |
+ | call_stmt
+ | The gcc.GimpleCall statememt for the function call
+ |
+ | callee
+ | The function that is called here, as a gcc.CallgraphNode
+ |
+ | caller
+ | The function that makes this call, as a gcc.CallgraphNode
+ |
+ | ----------------------------------------------------------------------
+ | Data and other attributes defined here:
+ |
+ | __new__ = <built-in method __new__ of type object>
+ | T.__new__(S, ...) -> a new object with type S, a subtype of T
+
+cgn:
+ cgn.decl: gcc.FunctionDecl('baz')
+ cgn.callers: []
+ cgn.callees: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+cgn:
+ cgn.decl: gcc.FunctionDecl('bar')
+ cgn.callers: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+ cgn.callees: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() bar ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() bar ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() bar ();
+cgn:
+ cgn.decl: gcc.FunctionDecl('foo')
+ cgn.callers: [gcc.CallgraphEdge(), gcc.CallgraphEdge(), gcc.CallgraphEdge()]
+ cgn.callees: []
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() foo ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() foo ();
+gcc.CallgraphEdge()
+e.caller: gcc.CallgraphNode()
+e.callee: gcc.CallgraphNode()
+e.call_stmt: gcc.GimpleCall() foo ();