[pypy-svn] r68885 - in pypy/branch/logging2/pypy/tool: . test

arigo at codespeak.net arigo at codespeak.net
Sun Nov 1 11:15:20 CET 2009


Author: arigo
Date: Sun Nov  1 11:15:20 2009
New Revision: 68885

Added:
   pypy/branch/logging2/pypy/tool/test/test_logparser.py   (contents, props changed)
Modified:
   pypy/branch/logging2/pypy/tool/logparser.py
Log:
Incorporate the JIT in the numbers, and display
summary histograms.


Modified: pypy/branch/logging2/pypy/tool/logparser.py
==============================================================================
--- pypy/branch/logging2/pypy/tool/logparser.py	(original)
+++ pypy/branch/logging2/pypy/tool/logparser.py	Sun Nov  1 11:15:20 2009
@@ -33,14 +33,65 @@
 def getsubcategories(log):
     return [entry for entry in log if entry[0] != 'debug_print']
 
+def gettimebounds(log):
+    # returns (mintime, maxtime)
+    maincats = getsubcategories(log)
+    return (maincats[0][1], maincats[-1][2])
+
+def gettotaltimes(log):
+    # returns a dict {'label' or None: totaltime}
+    def rectime(category1, timestart1, timestop1, subcats):
+        substartstop = []
+        for entry in getsubcategories(subcats):
+            rectime(*entry)
+            substartstop.append(entry[1:3])   # (start, stop)
+        # compute the total time for category1 as the part of the
+        # interval [timestart1, timestop1] that is not covered by
+        # any interval from one of the subcats.
+        mytime = 0
+        substartstop.sort()
+        for substart, substop in substartstop:
+            if substart >= timestop1:
+                break
+            if substart > timestart1:
+                mytime += substart - timestart1
+            if timestart1 < substop:
+                timestart1 = substop
+        if timestart1 < timestop1:
+            mytime += timestop1 - timestart1
+        #
+        try:
+            result[category1] += mytime
+        except KeyError:
+            result[category1] = mytime
+    #
+    result = {}
+    timestart0, timestop0 = gettimebounds(log)
+    rectime(None, timestart0, timestop0, log)
+    return result
+
 # ____________________________________________________________
 
 
 COLORS = {
+    None: (248, 248, 248),
     '': (160, 160, 160),
-    'gc-': (192, 0, 64),
+    'gc-': (224, 0, 0),
     'gc-minor': (192, 0, 16),
     'gc-collect': (255, 0, 0),
+    'jit-': (0, 224, 0),
+    'jit-running': (192, 255, 160),
+    'jit-tracing': (0, 255, 0),
+    'jit-optimize': (160, 255, 0),
+    'jit-backend': (0, 255, 144),
+    'jit-blackhole': (0, 160, 0),
+    }
+SUMMARY = {
+    None: 'normal execution',
+    '': 'other',
+    'gc-': 'gc',
+    'jit-': 'jit',
+    'jit-running': 'jit-running',
     }
 
 def getcolor(category):
@@ -73,14 +124,25 @@
     _cache[text] = sx, sy, texthoriz, textvert
     return _cache[text]
 
+def bevelrect(draw, (x1, y1, x2, y2), color):
+    if x2 <= x1:
+        x2 = x1 + 1   # minimal width
+    elif x2 >= x1 + 4:
+        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)
+
+# ----------
 
-def get_timeline_image(log, width=900, height=150):
+def get_timeline_image(log, width, height):
     from PIL import Image, ImageDraw
-    width = int(width)
-    height = int(height)
-    maincats = getsubcategories(log)
-    timestart0 = maincats[0][1]
-    timestop0  = maincats[-1][2]
+    timestart0, timestop0 = gettimebounds(log)
     assert timestop0 > timestart0
     timefactor = float(width) / (timestop0 - timestart0)
     #
@@ -91,21 +153,13 @@
             x2 = int((timestop1 - timestart0) * timefactor)
             y1 = (height - subheight) / 2
             y2 = y1 + subheight
+            y1 = int(y1)
+            y2 = int(y2)
             color = getcolor(category1)
             if firstx1 is None:
                 firstx1 = x1
-            if x2 <= x1:
-                x2 = x1 + 1   # minimal width
-            elif x2 >= x1 + 4:
-                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)
+            bevelrect(draw, (x1, y1, x2, y2), color)
+            subcats = getsubcategories(subcats)
             if subcats:
                 x2 = recdraw(subcats, subheight * 0.94) - 1
             sx, sy, texthoriz, textvert = getlabel(category1)
@@ -115,13 +169,131 @@
                 image.paste(textvert, (x1+1, y1+5), textvert)
         return firstx1
     #
-    image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
+    image = Image.new("RGBA", (width, height), (255, 255, 255, 0))
     draw = ImageDraw.Draw(image)
-    recdraw(maincats, height)
+    recdraw(getsubcategories(log), height)
     return image
 
-def draw_timeline_image(log, output=None, **kwds):
-    image = get_timeline_image(log, **kwds)
+# ----------
+
+def render_histogram(times, time0, labels, width, barheight):
+    # Render a histogram showing horizontal time bars are given by the
+    # 'times' dictionary.  Each entry has the label specified by 'labels',
+    # or by default the key used in 'times'.
+    from PIL import Image, ImageDraw
+    times = [(time, key) for (key, time) in times.items()]
+    times.sort()
+    times.reverse()
+    images = []
+    for time, key in times:
+        fraction = float(time) / time0
+        if fraction < 0.01:
+            break
+        color = getcolor(key)
+        image = Image.new("RGBA", (width, barheight), (255, 255, 255, 0))
+        draw = ImageDraw.Draw(image)
+        x2 = int(fraction * width)
+        bevelrect(draw, (0, 0, x2, barheight), color)
+        # draw the labels "x%" and "key"
+        percent = "%.1f%%" % (100.0 * fraction,)
+        s1x, s1y, textpercent, vtextpercent = getlabel(percent)
+        s2x, _, textlabel, _ = getlabel(labels.get(key, key))
+        t1x = 5
+        if t1x + s1x >= x2 - 3:
+            if t1x + s1y < x2 - 3:
+                textpercent = vtextpercent
+                s1x = s1y
+            else:
+                t1x = x2 + 6
+        t2x = t1x + s1x + 12
+        if t2x + s2x >= x2 - 3:
+            t2x = max(t2x, x2 + 8)
+        image.paste(textpercent, (t1x, 5), textpercent)
+        image.paste(textlabel,   (t2x, 5), textlabel)
+        images.append(image)
+    return combine(images, spacing=0, border=1, horizontal=False)
+
+def get_timesummary_single_image(totaltimes, totaltime0, componentdict,
+                                 width, barheight):
+    # Compress the totaltimes dict so that its only entries left are
+    # from componentdict.  We do that by taking the times assigned to
+    # subkeys in totaltimes and adding them to the superkeys specified
+    # in componentdict.
+    totaltimes = totaltimes.copy()
+    for key, value in totaltimes.items():
+        if key in componentdict:
+            continue
+        del totaltimes[key]
+        if key is not None:
+            while key not in componentdict:
+                key = key[:-1]
+            try:
+                totaltimes[key] += value
+            except KeyError:
+                totaltimes[key] = value
+    return render_histogram(totaltimes, totaltime0, componentdict,
+                            width, barheight)
+
+def get_timesummary_image(log, summarywidth, summarybarheight):
+    timestart0, timestop0 = gettimebounds(log)
+    totaltime0 = timestop0 - timestart0
+    totaltimes = gettotaltimes(log)
+    spacing = 50
+    width = (summarywidth - spacing) // 2
+    img1 = get_timesummary_single_image(totaltimes, totaltime0, SUMMARY,
+                                        width, summarybarheight)
+    if None in totaltimes:
+        del totaltimes[None]
+    img2 = render_histogram(totaltimes, totaltime0, {},
+                            width, summarybarheight)
+    return combine([img1, img2], spacing=spacing, horizontal=True)
+
+# ----------
+
+def combine(imagelist, spacing=50, border=0, horizontal=False):
+    if len(imagelist) <= 1 and not border:
+        return imagelist[0]
+    from PIL import Image, ImageDraw
+    wlist = [image.size[0] for image in imagelist]
+    hlist = [image.size[1] for image in imagelist]
+    if horizontal:
+        w = sum(wlist) + spacing*(len(imagelist)-1)
+        h = max(hlist)
+    else:
+        w = max(wlist)
+        h = sum(hlist) + spacing*(len(imagelist)-1)
+    w += 2*border
+    h += 2*border
+    bigimage = Image.new("RGBA", (w, h), (255, 255, 255, 0))
+    if border:
+        draw = ImageDraw.Draw(bigimage)
+        draw.rectangle((0, 0, w-1, border-1), fill=(0, 0, 0))
+        draw.rectangle((0, h-border, w-1, h-1), fill=(0, 0, 0))
+        draw.rectangle((0, 0, border-1, h-1), fill=(0, 0, 0))
+        draw.rectangle((w-1, 0, w-border, h-1), fill=(0, 0, 0))
+    x = border
+    y = border
+    for image in imagelist:
+        bigimage.paste(image, (x, y))
+        if horizontal:
+            x += image.size[0] + spacing
+        else:
+            y += image.size[1] + spacing
+    return bigimage
+
+def draw_timeline_image(log, output=None, mainwidth=3000, mainheight=150,
+                        summarywidth=850, summarybarheight=40):
+    mainwidth = int(mainwidth)
+    mainheight = int(mainheight)
+    summarywidth = int(summarywidth)
+    summarybarheight = int(summarybarheight)
+    images = []
+    if mainwidth > 0 and mainheight > 0:
+        images.append(get_timeline_image(log, mainwidth, mainheight))
+    if summarywidth > 0 and summarybarheight > 0:
+        images.append(get_timesummary_image(log, summarywidth,
+                                                 summarybarheight))
+    image = combine(images, horizontal=False)
     if output is None:
         image.save(sys.stdout, format='png')
     else:
@@ -131,7 +303,10 @@
 
 
 ACTIONS = {
-    'draw-time': (draw_timeline_image, ['width=', 'height=', 'output=']),
+    'draw-time': (draw_timeline_image, ['output=',
+                                        'mainwidth=', 'mainheight=',
+                                        'summarywidth=', 'summarybarheight=',
+                                        ]),
     }
 
 if __name__ == '__main__':

Added: pypy/branch/logging2/pypy/tool/test/test_logparser.py
==============================================================================
--- (empty file)
+++ pypy/branch/logging2/pypy/tool/test/test_logparser.py	Sun Nov  1 11:15:20 2009
@@ -0,0 +1,13 @@
+from pypy.tool.logparser import *
+
+def test_gettotaltimes():
+    result = gettotaltimes([
+        ('foo', 2, 17, [
+            ('bar', 4, 5, []),
+            ('bar', 7, 9, []),
+            ]),
+        ('bar', 20, 30, []),
+        ])
+    assert result == {None: 3,              # the hole between 17 and 20
+                      'foo': 15 - 1 - 2,
+                      'bar': 1 + 2 + 10}



More information about the Pypy-commit mailing list