2 import Image, ImageDraw, ImageFont
6 from binlog import read_history
7 from progressbar import ProgressBar, default_widgets
11 WHITE = (255, 255, 255)
12 font = ImageFont.load_default()
16 (225, 225, 225), #(170, 20, 10),
25 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000 ]
28 def __init__(self, duration, summary, transitions):
29 self.duration = duration
30 self.summary = summary
31 self.transitions = transitions
33 def parse_raw(filename):
34 hist = read_history(filename)
35 return Data(hist.transitions[-1][2] - hist.transitions[0][2],
39 def parse_cooked(filename):
42 if header != 'TraceVis-History0001':
43 print "Invalid header"
46 duration = struct.unpack_from('Q', f.read(8))[0]
48 state_count = struct.unpack_from('I', f.read(4))[0]
49 for i in range(state_count):
50 summary.append(struct.unpack_from('Q', f.read(8))[0])
52 # This method is actually faster than reading it all into a buffer and decoding
55 f.seek(0, os.SEEK_END)
56 n = (f.tell() - pos) / 10
59 #pb = ProgressBar('read input', n)
60 pb = ProgressBar(maxval=n, widgets=['read-input: ']+default_widgets)
68 s = struct.unpack_from('B', raw)[0]
69 r = struct.unpack_from('B', raw, 1)[0]
70 t = struct.unpack_from('Q', raw, 2)[0]
71 transitions.append((s, r, t))
74 return Data(duration, summary, transitions)
76 def draw(data, outfile):
77 total, summary, ts = data.duration, data.summary, data.transitions
79 W, H, HA, HB = 1600, 256, 64, 64
80 HZ = H + 4 + HA + 4 + HB
81 im = Image.new('RGBA', (W, HZ + 20))
82 d = ImageDraw.Draw(im)
86 a, b = 10*2.2e9, 12*2.2e9
87 ts = [(s, t-a) for s, t in ts if a <= t <= b ]
88 total_ms = total / CPU_SPEED * 1000
90 pp = 1.0 * total / (W*H)
91 pc_us = 1.0 * total / CPU_SPEED / W * 1e6
92 print "Parsed %d items, t=%f, t/pixel=%f"%(len(ts), total/CPU_SPEED, pp/CPU_SPEED*1000)
94 d.rectangle((0, H + 4, W, H + 4 + HA), (255, 255, 255))
98 # estimated progress of program run
101 # text progress indicator for graph generation
102 # pb = ProgressBar('draw main', W*H)
103 pb = ProgressBar(maxval=W*H, widgets=['draw main: ']+default_widgets)
106 # Flush events. We need to save them and draw them after filling the main area
107 # because otherwise they will be overwritten. Format is (x, y, r) to plot flush at,
108 # where r is reason code.
122 state, reason, time = ts[ti];
123 # States past this limit are actually events rather than state transitions
124 if state >= event_start:
125 events.append((x, y, reason))
129 q = (min(time, t1)-tx)/pp
134 ep += q * speedups[state]
140 color[0] = int(color[0])
141 color[1] = int(color[1])
142 color[2] = int(color[2])
144 d.point((x, y), fill=tuple(color))
150 for x, y, r in events:
151 d.ellipse((x-5, y-5, x+5, y+5), fill=(255, 222, 222), outline=(160, 10, 10))
152 ch = flush_reasons[r]
153 d.text((x-2, y-5), ch, fill=BLACK, font=font)
159 d.line((0, H+4+HA-1.0*H/epmax*HA, W, H+4+HA-1.0*H/epmax*HA), fill=(192, 192, 192))
160 d.line((0, H+4+HA-2.5*H/epmax*HA, W, H+4+HA-2.5*H/epmax*HA), fill=(192, 192, 192))
166 for j in range(max(0, i-3), min(W-1, i+3)):
171 ep_scaled = 1.0 * ep / epmax * HA
172 px = (i, H + 4 + HA - int(ep_scaled))
174 d.line((px, last), fill=(0, 0, 0))
176 #d.point((i, H + 4 + HA - int(ep_scaled)), fill=(0, 0, 0))
179 ppt = 1.0 * W / total
181 for dt, c in zip(summary, states):
184 x2 = int((t+dt) * ppt)
185 y2 = H + 4 + HA + 4 + HB
186 d.rectangle((x1, y1, x2, y2), fill=c)
192 divs = total_ms / timeslices_ms[i]
194 if i == len(timeslices_ms) - 1: break
197 timeslice = timeslices_ms[i]
204 d.line((x, y, x, y + 8), fill=c)
207 w, h = font.getsize(text)
208 d.text((x-w/2, HZ), text, fill=BLACK, font=font)
211 d.text((0, HZ), '[%.1fus]'%pc_us, fill=BLACK)
213 im.save(outfile, "PNG")
215 if __name__ == '__main__':
216 from optparse import OptionParser
217 parser = OptionParser()
218 parser.add_option('-c', action='store_true', dest='cooked', default=False,
219 help='process cooked format input file')
220 options, args = parser.parse_args()
222 if len(sys.argv) <= 2:
223 print >> sys.stderr, "usage: python vis.py infile outfile"
225 filename = sys.argv[1]
226 outfile = sys.argv[2]
227 if not outfile.endswith('.png'):
228 print >> sys.stderr, "warning: output filename does not end with .png; output is in png format."
231 data = parse_cooked(filename)
233 data = parse_raw(filename)