85
|
1 |
#
|
|
2 |
# Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
3 |
# All rights reserved.
|
|
4 |
# This component and the accompanying materials are made available
|
|
5 |
# under the terms of the License "Eclipse Public License v1.0"
|
|
6 |
# which accompanies this distribution, and is available
|
|
7 |
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
8 |
#
|
|
9 |
# Initial Contributors:
|
|
10 |
# Nokia Corporation - initial contribution.
|
|
11 |
#
|
|
12 |
# Contributors:
|
|
13 |
#
|
|
14 |
# Description:
|
|
15 |
#
|
|
16 |
# Raptor log visualisation program. Takes a raptor log as standard input
|
|
17 |
# and displays timelines that represent build progress and
|
|
18 |
# how much actual parallelism there is in the build.
|
|
19 |
# This program requires the pygame and PyOpenGL modules.
|
|
20 |
|
|
21 |
from OpenGL.GL import *
|
|
22 |
from OpenGL.GLU import *
|
|
23 |
import pygame
|
|
24 |
from pygame.locals import *
|
|
25 |
import time
|
|
26 |
|
|
27 |
class Timeline(object):
|
100
|
28 |
"""A bar representing a number of recipes which were executed in
|
|
29 |
time sequence. There is no guarantee about what host but in
|
|
30 |
theory they could have been executed on the same host."""
|
85
|
31 |
|
|
32 |
globalmax = 2.0
|
|
33 |
|
|
34 |
def __init__(self,ylevel):
|
|
35 |
self.maxtime = 0.0
|
|
36 |
self.recipes = []
|
|
37 |
self.ylevel = ylevel
|
|
38 |
|
|
39 |
def append(self, recipe):
|
100
|
40 |
"" add this recipe to this timeline if it happens after the latest recipe already in the timeline ""
|
85
|
41 |
if recipe.starttime + recipe.duration > self.maxtime:
|
|
42 |
self.maxtime = recipe.starttime + recipe.duration
|
|
43 |
if self.maxtime > Timeline.globalmax:
|
|
44 |
Timeline.globalmax = self.maxtime
|
|
45 |
else:
|
|
46 |
pass
|
|
47 |
|
|
48 |
self.recipes.append(recipe)
|
|
49 |
|
|
50 |
def draw(self):
|
|
51 |
glLoadIdentity()
|
|
52 |
self.xscale = 4.0 / Timeline.globalmax
|
|
53 |
|
|
54 |
glTranslatef(-2.0, -1.5, -6.0)
|
|
55 |
count = 0
|
|
56 |
for r in self.recipes:
|
|
57 |
if count % 2 == 0:
|
|
58 |
coloff=0.8
|
|
59 |
else:
|
|
60 |
coloff = 1.0
|
|
61 |
|
|
62 |
count += 1
|
|
63 |
r.draw(self.xscale, self.ylevel, coloff)
|
|
64 |
|
|
65 |
class Recipe(object):
|
100
|
66 |
"""Represents a task completed in a raptor build.
|
|
67 |
Drawn as a colour-coded bar with different
|
|
68 |
colours for the various recipe types."""
|
85
|
69 |
STAT_OK = 0
|
|
70 |
colours = {
|
|
71 |
'compile': (0.5,0.5,1.0),
|
|
72 |
'compile2object': (0.5,0.5,1.0),
|
|
73 |
'win32compile2object': (0.5,0.5,1.0),
|
|
74 |
'tools2linkexe': (0.5,1.0,0.5),
|
|
75 |
'link': (0.5,1.0,0.5),
|
100
|
76 |
'linkandpostlink': (0.5,1.0,0.5),
|
85
|
77 |
'win32stageonelink': (0.5,1.0,0.5),
|
|
78 |
'tools2lib': (0.5,1.0,1.0),
|
|
79 |
'win32stagetwolink': (1.0,0.1,1.0),
|
|
80 |
'postlink': (1.0,0.5,1.0)
|
|
81 |
}
|
|
82 |
|
|
83 |
def __init__(self, starttime, duration, name, status):
|
|
84 |
self.starttime = starttime
|
|
85 |
self.duration = duration
|
|
86 |
self.status = status
|
|
87 |
self.colour = (1.0, 1.0, 1.0)
|
|
88 |
if name in Recipe.colours:
|
|
89 |
self.colour = Recipe.colours[name]
|
|
90 |
else:
|
|
91 |
self.colour = (1.0,1.0,1.0)
|
|
92 |
self.name = name
|
|
93 |
|
|
94 |
def draw(self, scale, ylevel, coloff):
|
|
95 |
if self.status == Recipe.STAT_OK:
|
|
96 |
glColor4f(self.colour[0]*coloff, self.colour[1]*coloff, self.colour[2]*coloff,0.2)
|
|
97 |
else:
|
|
98 |
glColor4f(1.0*coloff, 0.6*coloff, 0.6*coloff,0.2)
|
|
99 |
|
|
100 |
|
|
101 |
x = self.starttime * scale
|
|
102 |
y = ylevel
|
|
103 |
x2 = x + self.duration * scale
|
|
104 |
y2 = ylevel + 0.2
|
|
105 |
glBegin(GL_QUADS)
|
|
106 |
glVertex3f(x, y, 0)
|
|
107 |
glVertex3f(x, y2, 0)
|
|
108 |
glVertex3f(x2, y2, 0)
|
|
109 |
glVertex3f(x2, y, 0)
|
|
110 |
glEnd()
|
|
111 |
|
|
112 |
|
|
113 |
def resize((width, height)):
|
|
114 |
if height==0:
|
|
115 |
height=1
|
|
116 |
glViewport(0, 0, width, height)
|
|
117 |
glMatrixMode(GL_PROJECTION)
|
|
118 |
glLoadIdentity()
|
|
119 |
gluPerspective(45, 1.0*width/height, 0.1, 100.0)
|
|
120 |
glMatrixMode(GL_MODELVIEW)
|
|
121 |
glLoadIdentity()
|
|
122 |
|
|
123 |
def init():
|
|
124 |
glShadeModel(GL_SMOOTH)
|
|
125 |
glClearColor(0.0, 0.0, 0.0, 0.0)
|
|
126 |
glClearDepth(1.0)
|
|
127 |
glEnable(GL_DEPTH_TEST)
|
|
128 |
glDepthFunc(GL_LEQUAL)
|
|
129 |
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
|
|
130 |
|
|
131 |
|
|
132 |
import sys
|
|
133 |
import re
|
|
134 |
|
|
135 |
def main():
|
|
136 |
|
|
137 |
video_flags = OPENGL|DOUBLEBUF
|
|
138 |
|
|
139 |
pygame.init()
|
|
140 |
pygame.display.set_mode((800,600), video_flags)
|
|
141 |
|
|
142 |
resize((800,600))
|
|
143 |
init()
|
|
144 |
|
|
145 |
frames = 0
|
|
146 |
ticks = pygame.time.get_ticks()
|
|
147 |
|
|
148 |
|
|
149 |
lines = 4
|
|
150 |
timelines = []
|
|
151 |
ylevel = 0.0
|
|
152 |
for i in xrange(0,4):
|
|
153 |
ylevel += 0.6
|
|
154 |
timelines.append(Timeline(ylevel))
|
|
155 |
|
|
156 |
f = sys.stdin
|
|
157 |
|
|
158 |
recipe_re = re.compile(".*<recipe name='([^']+)'.*")
|
|
159 |
time_re = re.compile(".*<time start='([0-9]+\.[0-9]+)' *elapsed='([0-9]+\.[0-9]+)'.*")
|
|
160 |
status_re = re.compile(".*<status exit='([^']*)'.*")
|
|
161 |
|
|
162 |
alternating = 0
|
|
163 |
start_time = 0.0
|
|
164 |
|
|
165 |
|
|
166 |
for l in f.xreadlines():
|
|
167 |
l2 = l.rstrip("\n")
|
|
168 |
rm = recipe_re.match(l2)
|
|
169 |
|
|
170 |
if rm is not None:
|
|
171 |
rname = rm.groups()[0]
|
|
172 |
continue
|
|
173 |
|
|
174 |
|
|
175 |
tm = time_re.match(l2)
|
|
176 |
if tm is not None:
|
|
177 |
s = float(tm.groups()[0])
|
|
178 |
elapsed = float(tm.groups()[1])
|
|
179 |
|
|
180 |
if start_time == 0.0:
|
|
181 |
start_time = s
|
|
182 |
|
|
183 |
s -= start_time
|
|
184 |
|
|
185 |
continue
|
|
186 |
|
|
187 |
sm = status_re.match(l2)
|
|
188 |
|
|
189 |
if sm is None:
|
|
190 |
continue
|
|
191 |
|
|
192 |
if sm.groups()[0] == 'ok':
|
|
193 |
status = 0
|
|
194 |
else:
|
|
195 |
status = int(sm.groups()[0])
|
|
196 |
|
|
197 |
olddiff = 999999999.0
|
|
198 |
tnum = 0
|
|
199 |
for t in timelines:
|
|
200 |
newdiff = s - t.maxtime
|
|
201 |
if newdiff < 0.0:
|
|
202 |
continue
|
|
203 |
if olddiff > newdiff:
|
|
204 |
dest_timeline = t
|
|
205 |
olddiff = newdiff
|
|
206 |
tnum += 1
|
|
207 |
|
|
208 |
dest_timeline.append(Recipe(s, elapsed, rname, status))
|
|
209 |
event = pygame.event.poll()
|
|
210 |
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
|
|
211 |
break
|
|
212 |
|
|
213 |
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
|
|
214 |
for t in timelines:
|
|
215 |
t.draw()
|
|
216 |
pygame.display.flip()
|
|
217 |
|
|
218 |
frames = frames+1
|
|
219 |
|
|
220 |
print "fps: %de" % ((frames*1000)/(pygame.time.get_ticks()-ticks))
|
|
221 |
event = pygame.event.wait()
|
|
222 |
|
|
223 |
|
|
224 |
if __name__ == '__main__': main()
|