Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / tracevis / vis.py
1 import os, struct, sys
2 import Image, ImageDraw, ImageFont
3
4 from config import *
5 from acts import *
6 from binlog import read_history
7 from progressbar import ProgressBar, default_widgets
8 from time import clock
9
10 BLACK = (0, 0, 0)
11 WHITE = (255, 255, 255)
12 font = ImageFont.load_default()
13
14 states = [
15     (0, 0, 0),
16     (225, 225, 225), #(170, 20, 10),
17     (225, 30, 12),
18     (140, 0, 255),
19     (0, 0, 255),
20     (180, 180, 30),
21     (0, 160, 30),
22 ]
23
24 timeslices_ms = [
25     1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000 ]
26
27 class Data(object):
28     def __init__(self, duration, summary, transitions):
29         self.duration = duration
30         self.summary = summary
31         self.transitions = transitions
32
33 def parse_raw(filename):
34     hist = read_history(filename)
35     return Data(hist.transitions[-1][2] - hist.transitions[0][2],
36                 hist.state_summary,
37                 hist.transitions)
38
39 def parse_cooked(filename):
40     f = open(filename)
41     header = f.read(20)
42     if header != 'TraceVis-History0001':
43         print "Invalid header"
44         sys.exit(1)
45
46     duration = struct.unpack_from('Q', f.read(8))[0]
47     summary = []
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])
51
52     # This method is actually faster than reading it all into a buffer and decoding
53     # from there.
54     pos = f.tell()
55     f.seek(0, os.SEEK_END)
56     n = (f.tell() - pos) / 10
57     f.seek(pos)
58
59     #pb = ProgressBar('read input', n)
60     pb = ProgressBar(maxval=n, widgets=['read-input: ']+default_widgets)
61     blip = n / 100
62
63     transitions = []
64     for i in range(n):
65         if i % blip == 0:
66             pb.update(i)
67         raw = f.read(10)
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))
72     pb.finish()
73     
74     return Data(duration, summary, transitions)
75
76 def draw(data, outfile):
77     total, summary, ts = data.duration, data.summary, data.transitions
78
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)
83
84     # Filter
85     if 0:
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
89
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)
93
94     d.rectangle((0, H + 4, W, H + 4 + HA), (255, 255, 255))
95
96     eps = []
97
98     # estimated progress of program run 
99     ep = 0
100
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)
104     blip = W*H//50
105
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.
109     events = []
110
111     ti = 0
112     for i in range(W*H):
113         if i % blip == 0:
114             pb.update(i)
115         x, y = i/H, i%H
116
117         t0 = pp * i
118         t1 = t0 + pp
119         color = [ 0, 0, 0 ]
120         tx = t0
121         while ti < len(ts):
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))
126                 ti += 1
127                 continue
128
129             q = (min(time, t1)-tx)/pp
130             c = states[state]
131             color[0] += q * c[0]
132             color[1] += q * c[1]
133             color[2] += q * c[2]
134             ep += q * speedups[state]
135             if time > t1:
136                 break
137             tx = time
138             ti += 1
139         
140         color[0] = int(color[0])
141         color[1] = int(color[1])
142         color[2] = int(color[2])
143
144         d.point((x, y), fill=tuple(color))
145
146         if i % H == H - 1:
147             eps.append(ep)
148             ep = 0
149
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)
154
155     pb.finish()
156
157     epmax = 2.5*H
158
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))
161
162     last = None
163     for i in range(W):
164         n = 0
165         sm = 0
166         for j in range(max(0, i-3), min(W-1, i+3)):
167             sm += eps[j]
168             n += 1
169         ep = sm / n
170
171         ep_scaled = 1.0 * ep / epmax * HA
172         px = (i, H + 4 + HA - int(ep_scaled))
173         if last:
174             d.line((px, last), fill=(0, 0, 0))
175         last = px
176         #d.point((i, H + 4 + HA - int(ep_scaled)), fill=(0, 0, 0))
177
178     t = 0
179     ppt = 1.0 * W / total
180     if True:
181         for dt, c in zip(summary, states):
182             x1 = int(t * ppt)
183             y1 = H + 4 + HA + 5 
184             x2 = int((t+dt) * ppt)
185             y2 = H + 4 + HA + 4 + HB
186             d.rectangle((x1, y1, x2, y2), fill=c)
187             t += dt
188
189     # Time lines
190     i = 0
191     while True:
192         divs = total_ms / timeslices_ms[i]
193         if divs < 15: break
194         if i == len(timeslices_ms) - 1: break
195         i += 1
196
197     timeslice = timeslices_ms[i]
198     t = timeslice
199     c = (128, 128, 128)
200     while t < total_ms:
201         x = t / total_ms * W
202         y = 0
203         while y < HZ:
204             d.line((x, y, x, y + 8), fill=c)
205             y += 12
206         text = '%d ms'%t
207         w, h = font.getsize(text)
208         d.text((x-w/2, HZ), text, fill=BLACK, font=font)
209         t += timeslice
210
211     d.text((0, HZ), '[%.1fus]'%pc_us, fill=BLACK)
212
213     im.save(outfile, "PNG")
214
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()
221
222     if len(sys.argv) <= 2:
223         print >> sys.stderr, "usage: python vis.py infile outfile"
224         sys.exit(1);
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."
229
230     if options.cooked:
231         data = parse_cooked(filename)
232     else:
233         data = parse_raw(filename)
234
235     draw(data, outfile)