[pypy-svn] r10294 - in pypy/dist: goal pypy/translator/tool

pedronis at codespeak.net pedronis at codespeak.net
Mon Apr 4 17:37:07 CEST 2005


Author: pedronis
Date: Mon Apr  4 17:37:07 2005
New Revision: 10294

Modified:
   pypy/dist/goal/translate_pypy.py
   pypy/dist/pypy/translator/tool/graphpage.py
Log:
added graph pages for only the class hierarchy and a localized call graph for functions
(showing just a single function and its direct callers and callees, there are extra nodes that give
access to the same for those)

there's a threshold for translate_pypy.py for the number of functions in translator set with -huge=.
Above the threshold we don't try to show the full call graph but only the localized one for the entry-point.

Command are accesible at the translate_pypy prompt to ask for a graph: see help graphs:

- for example classhier will show the class hierarchy graph,

- try showg intobject.W_IntObject.__mm_mul after translate_pypy targetpypy1 analysis for an
example of localized call graph.



Modified: pypy/dist/goal/translate_pypy.py
==============================================================================
--- pypy/dist/goal/translate_pypy.py	(original)
+++ pypy/dist/goal/translate_pypy.py	Mon Apr  4 17:37:07 2005
@@ -21,6 +21,8 @@
    -tcc       Equivalent to the envvar PYPY_CC='tcc -shared -o "%s.so" "%s.c"'
                   -- http://fabrice.bellard.free.fr/tcc/
    -no-d      Disable recording of debugging information
+   -huge=%    Threshold in the number of functions after which only a local call
+              graph and not a full one is displayed
 """
 import autopath, sys, threading, pdb, os
 
@@ -43,7 +45,7 @@
 # __________  Main  __________
 
 def analyse(target):
-    global t
+    global t, entry_point
 
     entry_point, inputtypes = target()
 
@@ -152,6 +154,7 @@
 if __name__ == '__main__':
 
     targetspec = 'targetpypy'
+    huge = 100
 
     options = {'-text': False,
                '-no-c': False,
@@ -172,6 +175,8 @@
         except ValueError:
             if os.path.isfile(arg+'.py'):
                 targetspec = arg
+            elif arg.startwith('-huge='):
+                huge = int(arg[6:])
             else:                
                 assert arg in options, "unknown option %r" % (arg,)
                 options[arg] = True
@@ -206,7 +211,7 @@
         print "don't know about", x
 
     def run_server():
-        from pypy.translator.tool.graphpage import TranslatorPage
+        from pypy.translator.tool import graphpage
         from pypy.translator.tool.pygame.graphclient import get_layout
         from pypy.translator.tool.pygame.graphdisplay import GraphDisplay
         import pygame
@@ -214,7 +219,12 @@
         if not options['-no-mark-some-objects']:
             find_someobjects(t, quiet=True)
 
-        display = GraphDisplay(get_layout(TranslatorPage(t)))
+        if len(t.functions) <= huge:
+            page = graphpage.TranslatorPage(t)
+        else:
+            page = graphpage.LocalizedCallGraphPage(t, entry_point)
+
+        display = GraphDisplay(get_layout(page))
         async_quit = display.async_quit
         def show(page):
             display.async_cmd(layout=get_layout(page))
@@ -236,27 +246,103 @@
                 return
             self.show(page)
 
-        def do_show(self, arg):
-            if '.' in arg:
-                name = ''
-                obj = None
-                for comp in arg.split('.'):
-                    name += comp
-                    obj = getattr(obj, comp, None)
-                    if obj is None:
-                        try:
-                            obj = __import__(name, {}, {}, ['*'])
-                        except ImportError:
-                            print "*** Not found: %s" % arg
-                            return
-                    name += '.'
-                if hasattr(obj, 'im_func'):
-                    obj = obj.im_func
-                if obj in t.flowgraphs:
-                    from pypy.translator.tool.graphpage import FlowGraphPage                    
-                    self._show(FlowGraphPage(t, [obj]))
+        def _importobj(self, fullname):
+            obj = None
+            name = ''
+            for comp in fullname.split('.'):
+                name += comp
+                obj = getattr(obj, comp, None)
+                if obj is None:
+                    try:
+                        obj = __import__(name, {}, {}, ['*'])
+                    except ImportError:
+                        raise NameError
+                name += '.'
+            return obj
+
+        TRYPREFIXES = ['','pypy.','pypy.objspace.','pypy.interpreter.', 'pypy.objspace.std.' ]
+
+        def _getobj(self, name):
+            if '.' in name:
+                for pfx in self.TRYPREFIXES:
+                    try:
+                        return self._importobj(pfx+name)
+                    except NameError:
+                        pass
+            try:
+                return self._getval(name)
+            except (NameError, AttributeError, LookupError):
+                print "*** Not found:", name
+            return None
+
+        def do_showg(self, arg):
+            """showg obj
+show graph for obj, obj can be an expression or a dotted name
+(in which case prefixing with some packages in pypy is tried (see help pypyprefixes)).
+if obj is a function or method, the localized call graph is shown;
+if obj is a class or ClassDef the class definition graph is shown"""            
+            from pypy.annotation.classdef import ClassDef
+            from pypy.translator.tool import graphpage            
+            obj = self._getobj(arg)
+            if obj is None:
+                return
+            if hasattr(obj, 'im_func'):
+                obj = obj.im_func
+            if obj in t.flowgraphs:
+                page = graphpage.LocalizedCallGraphPage(t, obj)
+            elif obj in getattr(t.annotator, 'getuserclasses', lambda: {})():
+                page = graphpage.ClassDefPage(t, t.annotator.getuserclasses()[obj])
+            elif isinstance(obj, ClassDef):
+                page = graphpage.ClassDefPage(t, obj)
             else:
                 print "*** Nothing to do"
+                return
+            self._show(page)
+
+        def do_flowg(self, arg):
+            """callg obj
+show flow graph for function obj, obj can be an expression or a dotted name
+(in which case prefixing with some packages in pypy is tried (see help pypyprefixes))"""            
+            import types
+            from pypy.translator.tool import graphpage                        
+            obj = self._getobj(arg)
+            if obj is None:
+                return
+            if hasattr(obj, 'im_func'):
+                obj = obj.im_func
+            if not isinstance(obj, types.FunctionType):
+                print "*** Not a function"
+                return
+            self._show(graphpage.FlowGraphPage(t, [obj]))
+
+        def do_callg(self, arg):
+            """callg obj
+show localized call-graph for function obj, obj can be an expression or a dotted name
+(in which case prefixing with some packages in pypy is tried (see help pypyprefixes))"""
+            import types
+            from pypy.translator.tool import graphpage                        
+            obj = self._getobj(arg)
+            if obj is None:
+                return
+            if hasattr(obj, 'im_func'):
+                obj = obj.im_func
+            if not isinstance(obj, types.FunctionType):
+                print "*** Not a function"
+                return
+            self._show(graphpage.LocalizedCallGraphPage(t, obj))
+
+        def do_classhier(self, arg):
+            """classhier
+show class hierarchy graph"""
+            from pypy.translator.tool import graphpage           
+            self._show(graphpage.ClassHierarchyPage(t))
+
+        def help_graphs(self):
+            print "graph commands are: showg, flowg, callg, classhier"
+
+        def help_pypyprefixes(self):
+            print "these prefixes are tried for dotted names in graph commands:"
+            print self.TRYPREFIXES
 
     def debug(got_error):
         pdb_plus_show = PdbPlusShow()

Modified: pypy/dist/pypy/translator/tool/graphpage.py
==============================================================================
--- pypy/dist/pypy/translator/tool/graphpage.py	(original)
+++ pypy/dist/pypy/translator/tool/graphpage.py	Mon Apr  4 17:37:07 2005
@@ -1,4 +1,4 @@
-import inspect
+import inspect, types
 from pypy.objspace.flow.model import traverse, Block
 from pypy.translator.tool.make_dot import DotGen, make_dot, make_dot_graphs
 from pypy.interpreter.pycode import CO_VARARGS, CO_VARKEYWORDS
@@ -208,7 +208,7 @@
             if isinstance(obj, ClassDef):
                 #data = '%s.%s' % (obj.cls.__module__, obj.cls.__name__)
                 data = repr(obj.cls)
-            else:
+            elif isinstance(obj, types.FunctionType):
                 func = obj
                 try:
                     source = inspect.getsource(func)
@@ -217,6 +217,8 @@
                 data = '%s:%d\n%s' % (func.func_globals.get('__name__', '?'),
                                       func.func_code.co_firstlineno,
                                       source.split('\n')[0])
+            else:
+                continue
             self.links[name] = data
 
     def get_blocked_functions(self, functions):
@@ -300,6 +302,70 @@
         self.compute_class_hieararchy(dotgen)
 
 
+class LocalizedCallGraphPage(BaseTranslatorPage):
+    """A GraphPage showing a the call graph between functions
+    as well as the class hierarchy."""
+
+    def graph_name(self, func0):
+        return 'LCG_%s' % nameof(func0)
+
+    def do_compute(self, dotgen, func0):
+        translator = self.translator
+
+        functions = {}
+
+        for f1, f2 in translator.callgraph.itervalues():
+            if f1 is func0 or f2 is func0:
+                dotgen.emit_edge(nameof(f1), nameof(f2))
+                functions[f1] = True
+                functions[f2] = True
+
+        functions = functions.keys()
+
+        # show the call graph
+        blocked_functions = self.get_blocked_functions(functions)
+
+        highlight_functions = getattr(translator, 'highlight_functions', {}) # XXX
+        for func in functions:
+            name = func.func_name
+            class_ = getattr(func, 'class_', None)
+            if class_ is not None:
+                name = '%s.%s' % (class_.__name__, name)
+            data = self.labelof(func, name)
+            if func in blocked_functions:
+                kw = {'fillcolor': 'red'}
+            elif func in highlight_functions:
+                kw = {'fillcolor': '#ffcccc'}
+            else:
+                kw = {}
+            dotgen.emit_node(nameof(func), label=data, shape="box", **kw)
+
+            if func is not func0:
+                lcg = 'LCG_%s' % nameof(func)
+                label = name+'...'
+                dotgen.emit_node(lcg, label=label)
+                dotgen.emit_edge(nameof(func), lcg)
+                self.links[label] = 'go to its localized call graph'
+                self.object_by_name[label] = func
+
+    def followlink(self, name):
+        if name.endswith('...'):
+            obj = self.object_by_name[name]
+            return LocalizedCallGraphPage(self.translator, obj)
+        return BaseTranslatorPage.followlink(self, name)
+
+class ClassHierarchyPage(BaseTranslatorPage):
+    """A GraphPage showing the class hierarchy."""
+
+    def graph_name(self):
+        return 'class_hierarchy'
+
+    def do_compute(self, dotgen):
+        translator = self.translator
+
+        # show the class hierarchy
+        self.compute_class_hieararchy(dotgen)
+
 def nameof(obj, cache={}):
     # NB. the purpose of the cache is not performance, but to ensure that
     # two objects that compare equal get the same name



More information about the Pypy-commit mailing list