[pypy-svn] r68879 - in pypy/branch/logging2/pypy: rlib rlib/test tool

arigo at codespeak.net arigo at codespeak.net
Sat Oct 31 17:42:51 CET 2009


Author: arigo
Date: Sat Oct 31 17:42:50 2009
New Revision: 68879

Added:
   pypy/branch/logging2/pypy/tool/logparser.py   (contents, props changed)
Modified:
   pypy/branch/logging2/pypy/rlib/debug.py
   pypy/branch/logging2/pypy/rlib/test/test_debug.py
Log:
Start pypy.tool.logparser, a tool to load and format the debugging logs.
So far, can be formatted as a PNG image giving the timeline (with PIL).


Modified: pypy/branch/logging2/pypy/rlib/debug.py
==============================================================================
--- pypy/branch/logging2/pypy/rlib/debug.py	(original)
+++ pypy/branch/logging2/pypy/rlib/debug.py	Sat Oct 31 17:42:50 2009
@@ -23,10 +23,25 @@
 class DebugLog(list):
     def debug_print(self, *args):
         self.append(('debug_print',) + args)
-    def debug_start(self, category):
-        self.append(('debug_start', category))
-    def debug_stop(self, category):
-        self.append(('debug_stop', category))
+    def debug_start(self, category, time=None):
+        self.append(('debug_start', category, time))
+    def debug_stop(self, category, time=None):
+        for i in xrange(len(self)-1, -1, -1):
+            if self[i][0] == 'debug_start':
+                assert self[i][1] == category, (
+                    "nesting error: starts with %r but stops with %r" %
+                    (self[i][1], category))
+                starttime = self[i][2]
+                if starttime is not None or time is not None:
+                    self[i:] = [(category, starttime, time, self[i+1:])]
+                else:
+                    self[i:] = [(category, self[i+1:])]
+                return
+        assert False, ("nesting error: no start corresponding to stop %r" %
+                       (category,))
+    def __repr__(self):
+        import pprint
+        return pprint.pformat(list(self))
 
 _log = None       # patched from tests to be an object of class DebugLog
                   # or compatible

Modified: pypy/branch/logging2/pypy/rlib/test/test_debug.py
==============================================================================
--- pypy/branch/logging2/pypy/rlib/test/test_debug.py	(original)
+++ pypy/branch/logging2/pypy/rlib/test/test_debug.py	Sat Oct 31 17:42:50 2009
@@ -57,9 +57,11 @@
             assert res == True
         finally:
             debug._log = None
-        assert ('debug_start', 'mycat') in dlog
-        assert ('debug_print', 'foo', 2, 'bar', 3) in dlog
-        assert ('debug_stop', 'mycat') in dlog
+        assert dlog == [
+            ("mycat", [
+                ('debug_print', 'foo', 2, 'bar', 3),
+                ]),
+            ]
 
         try:
             debug._log = dlog = debug.DebugLog()
@@ -67,9 +69,11 @@
             assert res == True
         finally:
             debug._log = None
-        assert ('debug_start', 'mycat') in dlog
-        assert ('debug_print', 'foo', 2, 'bar', 3) in dlog
-        assert ('debug_stop', 'mycat') in dlog
+        assert dlog == [
+            ("mycat", [
+                ('debug_print', 'foo', 2, 'bar', 3),
+                ]),
+            ]
 
 
 class TestLLType(DebugTests):

Added: pypy/branch/logging2/pypy/tool/logparser.py
==============================================================================
--- (empty file)
+++ pypy/branch/logging2/pypy/tool/logparser.py	Sat Oct 31 17:42:50 2009
@@ -0,0 +1,154 @@
+#! /usr/bin/env python
+"""
+Syntax:
+    python logparser.py <action> <logfilename> <options...>
+
+Actions:
+    draw-time   draw a timeline image of the log (format PPM)
+"""
+import autopath
+import sys, re
+from pypy.rlib.debug import DebugLog
+
+
+def parse_log_file(filename):
+    r_start = re.compile(r"\[([0-9a-f]+)\] \{([\w-]+)$")
+    r_stop  = re.compile(r"\[([0-9a-f]+)\] ([\w-]+)\}$")
+    log = DebugLog()
+    f = open(filename, 'r')
+    for line in f:
+        line = line.rstrip()
+        match = r_start.match(line)
+        if match:
+            log.debug_start(match.group(2), time=int(match.group(1), 16))
+            continue
+        match = r_stop.match(line)
+        if match:
+            log.debug_stop(match.group(2), time=int(match.group(1), 16))
+            continue
+        log.debug_print(line)
+    f.close()
+    return log
+
+def getsubcategories(log):
+    return [entry for entry in log if entry[0] != 'debug_print']
+
+# ____________________________________________________________
+
+
+COLORS = {
+    '': (160, 160, 160),
+    'gc-': (192, 0, 64),
+    'gc-minor': (192, 0, 16),
+    'gc-collect': (255, 0, 0),
+    }
+
+def getcolor(category):
+    while category not in COLORS:
+        category = category[:-1]
+    return COLORS[category]
+
+def getlightercolor((r, g, b)):
+    return ((r*2+255)//3, (g*2+255)//3, (b*2+255)//3)
+
+def getdarkercolor((r, g, b)):
+    return (r*2//3, g*2//3, b*2//3)
+
+def getlabel(text, _cache={}):
+    try:
+        return _cache[text]
+    except KeyError:
+        pass
+    from PIL import Image, ImageDraw
+    if None not in _cache:
+        image = Image.new("RGBA", (1, 1), (0, 0, 0, 0))
+        draw = ImageDraw.Draw(image)
+        _cache[None] = draw
+    else:
+        draw = _cache[None]
+    sx, sy = draw.textsize(text)
+    texthoriz = Image.new("RGBA", (sx, sy), (0, 0, 0, 0))
+    ImageDraw.Draw(texthoriz).text((0, 0), text, fill=(0, 0, 0))
+    textvert = texthoriz.rotate(90)
+    _cache[text] = sx, sy, texthoriz, textvert
+    return _cache[text]
+
+
+def get_timeline_image(log, width=900, height=150):
+    from PIL import Image, ImageDraw
+    width = int(width)
+    height = int(height)
+    maincats = getsubcategories(log)
+    timestart0 = maincats[0][1]
+    timestop0  = maincats[-1][2]
+    assert timestop0 > timestart0
+    timefactor = float(width) / (timestop0 - timestart0)
+    #
+    def recdraw(sublist, subheight):
+        firstx1 = None
+        for category1, timestart1, timestop1, subcats in sublist:
+            x1 = int((timestart1 - timestart0) * timefactor)
+            x2 = int((timestop1 - timestart0) * timefactor)
+            y1 = (height - subheight) / 2
+            y2 = y1 + subheight
+            color = getcolor(category1)
+            if firstx1 is None:
+                firstx1 = x1
+            if x2 <= x1 + 1:
+                x2 = x1 + 1   # not wide enough: don't show start and end lines
+            else:
+                draw.line([x1, y1+1, x1, y2-1], fill=getlightercolor(color))
+                x1 += 1
+                x2 -= 1
+                draw.line([x2, y1+1, x2, y2-1], fill=getdarkercolor(color))
+            draw.line([x1, y1, x2-1, y1], fill=getlightercolor(color))
+            y1 += 1
+            y2 -= 1
+            draw.line([x1, y2, x2-1, y2], fill=getdarkercolor(color))
+            draw.rectangle([x1, y1, x2-1, y2-1], fill=color)
+            if subcats:
+                x2 = recdraw(subcats, subheight * 0.94) - 1
+            sx, sy, texthoriz, textvert = getlabel(category1)
+            if sx <= x2-x1-8:
+                image.paste(texthoriz, (x1+5, y1+5), texthoriz)
+            elif sy <= x2-x1-2:
+                image.paste(textvert, (x1+1, y1+5), textvert)
+        return firstx1
+    #
+    image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
+    draw = ImageDraw.Draw(image)
+    recdraw(maincats, height)
+    return image
+
+def draw_timeline_image(log, output=None, **kwds):
+    image = get_timeline_image(log, **kwds)
+    if output is None:
+        image.save(sys.stdout, 'png')
+    else:
+        image.save(output)
+
+# ____________________________________________________________
+
+
+ACTIONS = {
+    'draw-time': (draw_timeline_image, ['width=', 'height=', 'output=']),
+    }
+
+if __name__ == '__main__':
+    import getopt
+    if len(sys.argv) < 2:
+        print __doc__
+        sys.exit(2)
+    action = sys.argv[1]
+    func, longopts = ACTIONS[action]
+    options, args = getopt.gnu_getopt(sys.argv[2:], '', longopts)
+    if len(args) != 1:
+        print __doc__
+        sys.exit(2)
+
+    kwds = {}
+    for name, value in options:
+        assert name.startswith('--')
+        kwds[name[2:]] = value
+    log = parse_log_file(args[0])
+    func(log, **kwds)



More information about the Pypy-commit mailing list