Deleted Added
full compact
schedgraph.py (187156) schedgraph.py (187358)
1#!/usr/local/bin/python
2
3# Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org>
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:

--- 10 unchanged lines hidden (view full) ---

19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
1#!/usr/local/bin/python
2
3# Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org>
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:

--- 10 unchanged lines hidden (view full) ---

19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# $FreeBSD: head/tools/sched/schedgraph.py 187156 2009-01-13 16:44:18Z jhb $
27# $FreeBSD: head/tools/sched/schedgraph.py 187358 2009-01-17 07:24:25Z jeff $
28
29import sys
30import re
28
29import sys
30import re
31import random
31from Tkinter import *
32
33# To use:
34# - Install the ports/x11-toolkits/py-tkinter package; e.g.
35# portinstall x11-toolkits/py-tkinter package
36# - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF; e.g.
37# options KTR
38# options KTR_ENTRIES=32768

--- 9 unchanged lines hidden (view full) ---

48# - While the workload is continuing (i.e. before it finishes), disable
49# KTR tracing by setting 'sysctl debug.ktr.mask=0'. This is necessary
50# to avoid a race condition while running ktrdump, i.e. the KTR ring buffer
51# will cycle a bit while ktrdump runs, and this confuses schedgraph because
52# the timestamps appear to go backwards at some point. Stopping KTR logging
53# while the workload is still running is to avoid wasting log entries on
54# "idle" time at the end.
55# - Dump the trace to a file: 'ktrdump -ct > ktr.out'
32from Tkinter import *
33
34# To use:
35# - Install the ports/x11-toolkits/py-tkinter package; e.g.
36# portinstall x11-toolkits/py-tkinter package
37# - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF; e.g.
38# options KTR
39# options KTR_ENTRIES=32768

--- 9 unchanged lines hidden (view full) ---

49# - While the workload is continuing (i.e. before it finishes), disable
50# KTR tracing by setting 'sysctl debug.ktr.mask=0'. This is necessary
51# to avoid a race condition while running ktrdump, i.e. the KTR ring buffer
52# will cycle a bit while ktrdump runs, and this confuses schedgraph because
53# the timestamps appear to go backwards at some point. Stopping KTR logging
54# while the workload is still running is to avoid wasting log entries on
55# "idle" time at the end.
56# - Dump the trace to a file: 'ktrdump -ct > ktr.out'
56# - Run the python script: 'python schedgraph.py ktr.out'
57# - Run the python script: 'python schedgraph.py ktr.out' optionally provide
58# your cpu frequency in ghz: 'python schedgraph.py ktr.out 2.4'
57#
58# To do:
59#
60# To do:
59# 1) Add a per-thread summary display
60# 2) Add bounding box style zoom.
61# 3) Click to center.
62# 4) Implement some sorting mechanism.
63# 5) Widget to display variable-range data (e.g. q length)
64# 6) Reorder rows, hide rows, etc.
65# 7) "Vertical rule" to help relate data in different rows
66# 8) Mouse-over popup of full thread/event/row lable (currently truncated)
67# 9) More visible anchors for popup event windows
61# Add a per-source summary display
62# Click to move.
63# Hide rows
64# "Vertical rule" to help relate data in different rows
65# Mouse-over popup of full thread/event/row label (currently truncated)
66# More visible anchors for popup event windows
68#
69# BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of
70# colours to represent them ;-)
67#
68# BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of
69# colours to represent them ;-)
71# 2) Extremely short traces may cause a crash because the code
72# assumes there is always at least one stathz entry logged, and
73# the number of such events is used as a denominator
74
70
71eventcolors = [
72 ("count", "red"),
73 ("running", "green"),
74 ("idle", "grey"),
75 ("yielding", "yellow"),
76 ("swapped", "violet"),
77 ("suspended", "purple"),
78 ("iwait", "grey"),
79 ("sleep", "blue"),
80 ("blocked", "dark red"),
81 ("runq add", "yellow"),
82 ("runq rem", "yellow"),
83 ("thread exit", "grey"),
84 ("proc exit", "grey"),
85 ("callwheel idle", "grey"),
86 ("callout running", "green"),
87 ("lock acquire", "blue"),
88 ("lock contest", "purple"),
89 ("failed lock try", "red"),
90 ("lock release", "grey"),
91 ("tick", "black"),
92 ("prio", "black"),
93 ("lend prio", "black"),
94 ("wokeup", "black")
95]
96
97cpucolors = [
98 ("CPU 0", "light grey"),
99 ("CPU 1", "dark grey"),
100 ("CPU 2", "light blue"),
101 ("CPU 3", "light pink"),
102 ("CPU 4", "blanched almond"),
103 ("CPU 5", "slate grey"),
104 ("CPU 6", "tan"),
105 ("CPU 7", "thistle"),
106 ("CPU 8", "white")
107]
108
109colors = [
110 "white", "thistle", "blanched almond", "tan", "chartreuse",
111 "dark red", "red", "pale violet red", "pink", "light pink",
112 "dark orange", "orange", "coral", "light coral",
113 "goldenrod", "gold", "yellow", "light yellow",
114 "dark green", "green", "light green", "light sea green",
115 "dark blue", "blue", "light blue", "steel blue", "light slate blue",
116 "dark violet", "violet", "purple", "blue violet",
117 "dark grey", "slate grey", "light grey",
118 "black",
119]
120colors.sort()
121
75ticksps = None
76status = None
122ticksps = None
123status = None
77configtypes = []
124colormap = None
125ktrfile = None
126clockfreq = None
127sources = []
78lineno = -1
79
128lineno = -1
129
130class Colormap:
131 def __init__(self, table):
132 self.table = table
133 self.map = {}
134 for entry in table:
135 self.map[entry[0]] = entry[1]
136
137 def lookup(self, name):
138 try:
139 color = self.map[name]
140 except:
141 color = colors[random.randrange(0, len(colors))]
142 print "Picking random color", color, "for", name
143 self.map[name] = color
144 self.table.append((name, color))
145 return (color)
146
80def ticks2sec(ticks):
81 us = ticksps / 1000000
82 ticks /= us
83 if (ticks < 1000):
84 return (str(ticks) + "us")
85 ticks /= 1000
86 if (ticks < 1000):
87 return (str(ticks) + "ms")

--- 31 unchanged lines hidden (view full) ---

119
120 def clear(self):
121 self.label.config(text="")
122
123 def startup(self, str):
124 self.set(str)
125 root.update()
126
147def ticks2sec(ticks):
148 us = ticksps / 1000000
149 ticks /= us
150 if (ticks < 1000):
151 return (str(ticks) + "us")
152 ticks /= 1000
153 if (ticks < 1000):
154 return (str(ticks) + "ms")

--- 31 unchanged lines hidden (view full) ---

186
187 def clear(self):
188 self.label.config(text="")
189
190 def startup(self, str):
191 self.set(str)
192 root.update()
193
127class EventConf(Frame):
128 def __init__(self, master, name, color, enabled):
194class ColorConf(Frame):
195 def __init__(self, master, name, color):
129 Frame.__init__(self, master)
196 Frame.__init__(self, master)
197 if (graph.getstate(name) == "hidden"):
198 enabled = 0
199 else:
200 enabled = 1
130 self.name = name
131 self.color = StringVar()
132 self.color_default = color
133 self.color_current = color
134 self.color.set(color)
135 self.enabled = IntVar()
136 self.enabled_default = enabled
137 self.enabled_current = enabled
138 self.enabled.set(enabled)
139 self.draw()
140
141 def draw(self):
142 self.label = Label(self, text=self.name, anchor=W)
143 self.sample = Canvas(self, width=24, height=24,
144 bg='grey')
145 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
146 fill=self.color.get())
201 self.name = name
202 self.color = StringVar()
203 self.color_default = color
204 self.color_current = color
205 self.color.set(color)
206 self.enabled = IntVar()
207 self.enabled_default = enabled
208 self.enabled_current = enabled
209 self.enabled.set(enabled)
210 self.draw()
211
212 def draw(self):
213 self.label = Label(self, text=self.name, anchor=W)
214 self.sample = Canvas(self, width=24, height=24,
215 bg='grey')
216 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
217 fill=self.color.get())
147 self.list = OptionMenu(self, self.color,
148 "dark red", "red", "pink",
149 "dark orange", "orange",
150 "yellow", "light yellow",
151 "dark green", "green", "light green",
152 "dark blue", "blue", "light blue",
153 "dark violet", "violet", "purple",
154 "dark grey", "light grey",
155 "white", "black",
156 command=self.setcolor)
218 self.list = OptionMenu(self, self.color, command=self.setcolor,
219 *colors)
157 self.checkbox = Checkbutton(self, text="enabled",
158 variable=self.enabled)
159 self.label.grid(row=0, column=0, sticky=E+W)
160 self.sample.grid(row=0, column=1)
161 self.list.grid(row=0, column=2, sticky=E+W)
162 self.checkbox.grid(row=0, column=3)
163 self.columnconfigure(0, weight=1)
220 self.checkbox = Checkbutton(self, text="enabled",
221 variable=self.enabled)
222 self.label.grid(row=0, column=0, sticky=E+W)
223 self.sample.grid(row=0, column=1)
224 self.list.grid(row=0, column=2, sticky=E+W)
225 self.checkbox.grid(row=0, column=3)
226 self.columnconfigure(0, weight=1)
164 self.columnconfigure(2, minsize=110)
227 self.columnconfigure(2, minsize=150)
165
166 def setcolor(self, color):
167 self.color.set(color)
168 self.sample.itemconfigure(self.rect, fill=color)
169
170 def apply(self):
171 cchange = 0
172 echange = 0

--- 8 unchanged lines hidden (view full) ---

181 graph.setcolor(self.name, self.color_current)
182 else:
183 graph.hide(self.name)
184 return
185 if (cchange != 0):
186 graph.setcolor(self.name, self.color_current)
187
188 def revert(self):
228
229 def setcolor(self, color):
230 self.color.set(color)
231 self.sample.itemconfigure(self.rect, fill=color)
232
233 def apply(self):
234 cchange = 0
235 echange = 0

--- 8 unchanged lines hidden (view full) ---

244 graph.setcolor(self.name, self.color_current)
245 else:
246 graph.hide(self.name)
247 return
248 if (cchange != 0):
249 graph.setcolor(self.name, self.color_current)
250
251 def revert(self):
189 self.setcolor(self.color_current)
190 self.enabled.set(self.enabled_current)
191
192 def default(self):
193 self.setcolor(self.color_default)
194 self.enabled.set(self.enabled_default)
195
252 self.setcolor(self.color_default)
253 self.enabled.set(self.enabled_default)
254
196class EventConfigure(Toplevel):
197 def __init__(self):
255class ColorConfigure(Toplevel):
256 def __init__(self, table, name):
198 Toplevel.__init__(self)
199 self.resizable(0, 0)
257 Toplevel.__init__(self)
258 self.resizable(0, 0)
200 self.title("Event Configuration")
201 self.items = LabelFrame(self, text="Event Type")
259 self.title(name)
260 self.items = LabelFrame(self, text="Item Type")
202 self.buttons = Frame(self)
203 self.drawbuttons()
204 self.items.grid(row=0, column=0, sticky=E+W)
205 self.columnconfigure(0, weight=1)
206 self.buttons.grid(row=1, column=0, sticky=E+W)
207 self.types = []
208 self.irow = 0
261 self.buttons = Frame(self)
262 self.drawbuttons()
263 self.items.grid(row=0, column=0, sticky=E+W)
264 self.columnconfigure(0, weight=1)
265 self.buttons.grid(row=1, column=0, sticky=E+W)
266 self.types = []
267 self.irow = 0
209 for type in configtypes:
210 self.additem(type.name, type.color, type.enabled)
268 for type in table:
269 color = graph.getcolor(type[0])
270 if (color != ""):
271 self.additem(type[0], color)
211
272
212 def additem(self, name, color, enabled=1):
213 item = EventConf(self.items, name, color, enabled)
273 def additem(self, name, color):
274 item = ColorConf(self.items, name, color)
214 self.types.append(item)
215 item.grid(row=self.irow, column=0, sticky=E+W)
216 self.irow += 1
217
218 def drawbuttons(self):
219 self.apply = Button(self.buttons, text="Apply",
220 command=self.apress)
275 self.types.append(item)
276 item.grid(row=self.irow, column=0, sticky=E+W)
277 self.irow += 1
278
279 def drawbuttons(self):
280 self.apply = Button(self.buttons, text="Apply",
281 command=self.apress)
221 self.revert = Button(self.buttons, text="Revert",
282 self.default = Button(self.buttons, text="Revert",
222 command=self.rpress)
283 command=self.rpress)
223 self.default = Button(self.buttons, text="Default",
224 command=self.dpress)
225 self.apply.grid(row=0, column=0, sticky=E+W)
284 self.apply.grid(row=0, column=0, sticky=E+W)
226 self.revert.grid(row=0, column=1, sticky=E+W)
227 self.default.grid(row=0, column=2, sticky=E+W)
285 self.default.grid(row=0, column=1, sticky=E+W)
228 self.buttons.columnconfigure(0, weight=1)
229 self.buttons.columnconfigure(1, weight=1)
286 self.buttons.columnconfigure(0, weight=1)
287 self.buttons.columnconfigure(1, weight=1)
230 self.buttons.columnconfigure(2, weight=1)
231
232 def apress(self):
233 for item in self.types:
234 item.apply()
235
236 def rpress(self):
237 for item in self.types:
238 item.revert()
239
288
289 def apress(self):
290 for item in self.types:
291 item.apply()
292
293 def rpress(self):
294 for item in self.types:
295 item.revert()
296
240 def dpress(self):
241 for item in self.types:
242 item.default()
243
244class EventView(Toplevel):
245 def __init__(self, event, canvas):
246 Toplevel.__init__(self)
247 self.resizable(0, 0)
248 self.title("Event")
249 self.event = event
297class EventView(Toplevel):
298 def __init__(self, event, canvas):
299 Toplevel.__init__(self)
300 self.resizable(0, 0)
301 self.title("Event")
302 self.event = event
250 self.frame = Frame(self)
251 self.frame.grid(row=0, column=0, sticky=N+S+E+W)
252 self.buttons = Frame(self)
303 self.buttons = Frame(self)
253 self.buttons.grid(row=1, column=0, sticky=E+W)
304 self.buttons.grid(row=0, column=0, sticky=E+W)
305 self.frame = Frame(self)
306 self.frame.grid(row=1, column=0, sticky=N+S+E+W)
254 self.canvas = canvas
255 self.drawlabels()
256 self.drawbuttons()
257 event.displayref(canvas)
258 self.bind("<Destroy>", self.destroycb)
259
260 def destroycb(self, event):
261 self.unbind("<Destroy>")

--- 5 unchanged lines hidden (view full) ---

267 def clearlabels(self):
268 for label in self.frame.grid_slaves():
269 label.grid_remove()
270
271 def drawlabels(self):
272 ypos = 0
273 labels = self.event.labels()
274 while (len(labels) < 7):
307 self.canvas = canvas
308 self.drawlabels()
309 self.drawbuttons()
310 event.displayref(canvas)
311 self.bind("<Destroy>", self.destroycb)
312
313 def destroycb(self, event):
314 self.unbind("<Destroy>")

--- 5 unchanged lines hidden (view full) ---

320 def clearlabels(self):
321 for label in self.frame.grid_slaves():
322 label.grid_remove()
323
324 def drawlabels(self):
325 ypos = 0
326 labels = self.event.labels()
327 while (len(labels) < 7):
275 labels.append(("", "", 0))
328 labels.append(("", ""))
276 for label in labels:
329 for label in labels:
277 name, value, linked = label
330 name, value = label
331 linked = 0
332 if (name == "linkedto"):
333 linked = 1
278 l = Label(self.frame, text=name, bd=1, width=15,
279 relief=SUNKEN, anchor=W)
280 if (linked):
281 fgcolor = "blue"
282 else:
283 fgcolor = "black"
284 r = Label(self.frame, text=value, bd=1,
285 relief=SUNKEN, anchor=W, fg=fgcolor)

--- 22 unchanged lines hidden (view full) ---

308
309 def npress(self):
310 EventView(self.event, self.canvas)
311
312 def bpress(self):
313 prev = self.event.prev()
314 if (prev == None):
315 return
334 l = Label(self.frame, text=name, bd=1, width=15,
335 relief=SUNKEN, anchor=W)
336 if (linked):
337 fgcolor = "blue"
338 else:
339 fgcolor = "black"
340 r = Label(self.frame, text=value, bd=1,
341 relief=SUNKEN, anchor=W, fg=fgcolor)

--- 22 unchanged lines hidden (view full) ---

364
365 def npress(self):
366 EventView(self.event, self.canvas)
367
368 def bpress(self):
369 prev = self.event.prev()
370 if (prev == None):
371 return
316 while (prev.real == 0):
372 while (prev.type == "pad"):
317 prev = prev.prev()
318 if (prev == None):
319 return
320 self.newevent(prev)
321
322 def fpress(self):
323 next = self.event.next()
324 if (next == None):
325 return
373 prev = prev.prev()
374 if (prev == None):
375 return
376 self.newevent(prev)
377
378 def fpress(self):
379 next = self.event.next()
380 if (next == None):
381 return
326 while (next.real == 0):
382 while (next.type == "pad"):
327 next = next.next()
328 if (next == None):
329 return
330 self.newevent(next)
331
332 def linkpress(self, wevent):
333 event = self.event.getlinked()
334 if (event != None):
335 self.newevent(event)
336
337class Event:
383 next = next.next()
384 if (next == None):
385 return
386 self.newevent(next)
387
388 def linkpress(self, wevent):
389 event = self.event.getlinked()
390 if (event != None):
391 self.newevent(event)
392
393class Event:
338 name = "none"
339 color = "grey"
340 def __init__(self, source, cpu, timestamp, last=0):
394 def __init__(self, source, name, cpu, timestamp, attrs):
341 self.source = source
395 self.source = source
396 self.name = name
342 self.cpu = cpu
343 self.timestamp = int(timestamp)
397 self.cpu = cpu
398 self.timestamp = int(timestamp)
344 self.entries = []
345 self.real = 1
399 self.attrs = attrs
346 self.idx = None
400 self.idx = None
347 self.state = 0
348 self.item = None
349 self.dispcnt = 0
401 self.item = None
402 self.dispcnt = 0
350 self.linked = None
351 self.recno = lineno
403 self.recno = lineno
352 if (last):
353 source.lastevent(self)
354 else:
355 source.event(self)
356
357 def status(self):
358 statstr = self.name + " " + self.source.name
359 statstr += " on: cpu" + str(self.cpu)
360 statstr += " at: " + str(self.timestamp)
404
405 def status(self):
406 statstr = self.name + " " + self.source.name
407 statstr += " on: cpu" + str(self.cpu)
408 statstr += " at: " + str(self.timestamp)
361 statstr += self.stattxt()
409 statstr += " attributes: "
410 for i in range(0, len(self.attrs)):
411 attr = self.attrs[i]
412 statstr += attr[0] + ": " + str(attr[1])
413 if (i != len(self.attrs) - 1):
414 statstr += ", "
362 status.set(statstr)
363
415 status.set(statstr)
416
364 def stattxt(self):
365 return ""
366
367 def textadd(self, tuple):
368 pass
369 self.entries.append(tuple)
370
371 def labels(self):
417 def labels(self):
372 return [("Source:", self.source.name, 0),
373 ("Event:", self.name, 0),
374 ("CPU:", self.cpu, 0),
375 ("Timestamp:", self.timestamp, 0),
376 ("Record: ", self.recno, 0)
377 ] + self.entries
378 def mouseenter(self, canvas, item):
418 return [("Source", self.source.name),
419 ("Event", self.name),
420 ("CPU", self.cpu),
421 ("Timestamp", self.timestamp),
422 ("KTR Line ", self.recno)
423 ] + self.attrs
424
425 def mouseenter(self, canvas):
379 self.displayref(canvas)
380 self.status()
381
426 self.displayref(canvas)
427 self.status()
428
382 def mouseexit(self, canvas, item):
429 def mouseexit(self, canvas):
383 self.displayunref(canvas)
384 status.clear()
385
430 self.displayunref(canvas)
431 status.clear()
432
386 def mousepress(self, canvas, item):
433 def mousepress(self, canvas):
387 EventView(self, canvas)
388
434 EventView(self, canvas)
435
436 def draw(self, canvas, xpos, ypos, item):
437 self.item = item
438 if (item != None):
439 canvas.items[item] = self
440
441 def move(self, canvas, x, y):
442 if (self.item == None):
443 return;
444 canvas.move(self.item, x, y);
445
389 def next(self):
390 return self.source.eventat(self.idx + 1)
391
446 def next(self):
447 return self.source.eventat(self.idx + 1)
448
449 def nexttype(self, type):
450 next = self.next()
451 while (next != None and next.type != type):
452 next = next.next()
453 return (next)
454
392 def prev(self):
393 return self.source.eventat(self.idx - 1)
394
395 def displayref(self, canvas):
396 if (self.dispcnt == 0):
397 canvas.itemconfigure(self.item, width=2)
398 self.dispcnt += 1
399
400 def displayunref(self, canvas):
401 self.dispcnt -= 1
402 if (self.dispcnt == 0):
403 canvas.itemconfigure(self.item, width=0)
404 canvas.tag_raise("point", "state")
405
406 def getlinked(self):
455 def prev(self):
456 return self.source.eventat(self.idx - 1)
457
458 def displayref(self, canvas):
459 if (self.dispcnt == 0):
460 canvas.itemconfigure(self.item, width=2)
461 self.dispcnt += 1
462
463 def displayunref(self, canvas):
464 self.dispcnt -= 1
465 if (self.dispcnt == 0):
466 canvas.itemconfigure(self.item, width=0)
467 canvas.tag_raise("point", "state")
468
469 def getlinked(self):
407 return self.linked.findevent(self.timestamp)
470 for attr in self.attrs:
471 if (attr[0] != "linkedto"):
472 continue
473 source = ktrfile.findid(attr[1])
474 return source.findevent(self.timestamp)
475 return None
408
409class PointEvent(Event):
476
477class PointEvent(Event):
410 def __init__(self, thread, cpu, timestamp, last=0):
411 Event.__init__(self, thread, cpu, timestamp, last)
478 type = "point"
479 def __init__(self, source, name, cpu, timestamp, attrs):
480 Event.__init__(self, source, name, cpu, timestamp, attrs)
412
413 def draw(self, canvas, xpos, ypos):
481
482 def draw(self, canvas, xpos, ypos):
483 color = colormap.lookup(self.name)
414 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
484 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
415 fill=self.color, tags=("all", "point", "event")
416 + (self.name,), width=0)
417 canvas.events[l] = self
418 self.item = l
419 if (self.enabled == 0):
420 canvas.itemconfigure(l, state="hidden")
485 fill=color, tags=("all", "point", "event", self.name),
486 width=0)
487 Event.draw(self, canvas, xpos, ypos, l)
421
488
422 return (xpos)
489 return xpos
423
424class StateEvent(Event):
490
491class StateEvent(Event):
425 def __init__(self, thread, cpu, timestamp, last=0):
426 Event.__init__(self, thread, cpu, timestamp, last)
427 self.duration = 0
428 self.skipnext = 0
429 self.skipself = 0
430 self.state = 1
492 type = "state"
493 def __init__(self, source, name, cpu, timestamp, attrs):
494 Event.__init__(self, source, name, cpu, timestamp, attrs)
431
432 def draw(self, canvas, xpos, ypos):
495
496 def draw(self, canvas, xpos, ypos):
433 next = self.nextstate()
434 if (self.skipself == 1 or next == None):
497 next = self.nexttype("state")
498 if (next == None):
435 return (xpos)
499 return (xpos)
436 while (self.skipnext):
437 skipped = next
438 next.skipself = 1
439 next.real = 0
440 next = next.nextstate()
441 if (next == None):
442 next = skipped
443 self.skipnext -= 1
444 self.duration = next.timestamp - self.timestamp
445 if (self.duration < 0):
446 self.duration = 0
500 duration = next.timestamp - self.timestamp
501 self.attrs.insert(0, ("duration", ticks2sec(duration)))
502 color = colormap.lookup(self.name)
503 if (duration < 0):
504 duration = 0
447 print "Unsynchronized timestamp"
448 print self.cpu, self.timestamp
449 print next.cpu, next.timestamp
505 print "Unsynchronized timestamp"
506 print self.cpu, self.timestamp
507 print next.cpu, next.timestamp
450 delta = self.duration / canvas.ratio
508 delta = duration / canvas.ratio
451 l = canvas.create_rectangle(xpos, ypos,
509 l = canvas.create_rectangle(xpos, ypos,
452 xpos + delta, ypos - 10, fill=self.color, width=0,
453 tags=("all", "state", "event") + (self.name,))
454 canvas.events[l] = self
455 self.item = l
456 if (self.enabled == 0):
457 canvas.itemconfigure(l, state="hidden")
510 xpos + delta, ypos - 10, fill=color, width=0,
511 tags=("all", "state", "event", self.name))
512 Event.draw(self, canvas, xpos, ypos, l)
458
459 return (xpos + delta)
460
513
514 return (xpos + delta)
515
461 def stattxt(self):
462 return " duration: " + ticks2sec(self.duration)
516class CountEvent(Event):
517 type = "count"
518 def __init__(self, source, count, cpu, timestamp, attrs):
519 count = int(count)
520 self.count = count
521 Event.__init__(self, source, "count", cpu, timestamp, attrs)
463
522
464 def nextstate(self):
465 next = self.next()
466 while (next != None and next.state == 0):
467 next = next.next()
468 return (next)
469
470 def labels(self):
471 return [("Source:", self.source.name, 0),
472 ("Event:", self.name, 0),
473 ("Timestamp:", self.timestamp, 0),
474 ("CPU:", self.cpu, 0),
475 ("Record:", self.recno, 0),
476 ("Duration:", ticks2sec(self.duration), 0)
477 ] + self.entries
478
479class Count(Event):
480 name = "Count"
481 color = "red"
482 enabled = 1
483 def __init__(self, source, cpu, timestamp, count):
484 self.count = int(count)
485 Event.__init__(self, source, cpu, timestamp)
486 self.duration = 0
487 self.textadd(("count:", self.count, 0))
488
489 def draw(self, canvas, xpos, ypos):
523 def draw(self, canvas, xpos, ypos):
490 next = self.next()
491 self.duration = next.timestamp - self.timestamp
492 delta = self.duration / canvas.ratio
524 next = self.nexttype("count")
525 if (next == None):
526 return (xpos)
527 color = colormap.lookup("count")
528 duration = next.timestamp - self.timestamp
529 self.attrs.insert(0, ("count", self.count))
530 self.attrs.insert(1, ("duration", ticks2sec(duration)))
531 delta = duration / canvas.ratio
493 yhight = self.source.yscale() * self.count
494 l = canvas.create_rectangle(xpos, ypos - yhight,
532 yhight = self.source.yscale() * self.count
533 l = canvas.create_rectangle(xpos, ypos - yhight,
495 xpos + delta, ypos, fill=self.color, width=0,
496 tags=("all", "count", "event") + (self.name,))
497 canvas.events[l] = self
498 self.item = l
499 if (self.enabled == 0):
500 canvas.itemconfigure(l, state="hidden")
534 xpos + delta, ypos, fill=color, width=0,
535 tags=("all", "count", "event", self.name))
536 Event.draw(self, canvas, xpos, ypos, l)
501 return (xpos + delta)
502
537 return (xpos + delta)
538
503 def stattxt(self):
504 return " count: " + str(self.count)
505
506configtypes.append(Count)
507
508class Running(StateEvent):
509 name = "running"
510 color = "green"
511 enabled = 1
512 def __init__(self, thread, cpu, timestamp, prio):
513 StateEvent.__init__(self, thread, cpu, timestamp)
514 self.prio = prio
515 self.textadd(("prio:", self.prio, 0))
516
517configtypes.append(Running)
518
519class Idle(StateEvent):
520 name = "idle"
521 color = "grey"
522 enabled = 0
523 def __init__(self, thread, cpu, timestamp, prio):
524 StateEvent.__init__(self, thread, cpu, timestamp)
525 self.prio = prio
526 self.textadd(("prio:", self.prio, 0))
527
528configtypes.append(Idle)
529
530class Yielding(StateEvent):
531 name = "yielding"
532 color = "yellow"
533 enabled = 1
534 def __init__(self, thread, cpu, timestamp, prio):
535 StateEvent.__init__(self, thread, cpu, timestamp)
536 self.skipnext = 0
537 self.prio = prio
538 self.textadd(("prio:", self.prio, 0))
539
540configtypes.append(Yielding)
541
542class Swapped(StateEvent):
543 name = "swapped"
544 color = "violet"
545 enabled = 1
546 def __init__(self, thread, cpu, timestamp, prio):
547 StateEvent.__init__(self, thread, cpu, timestamp)
548 self.prio = prio
549 self.textadd(("prio:", self.prio, 0))
550
551configtypes.append(Swapped)
552
553class Suspended(StateEvent):
554 name = "suspended"
555 color = "purple"
556 enabled = 1
557 def __init__(self, thread, cpu, timestamp, prio):
558 StateEvent.__init__(self, thread, cpu, timestamp)
559 self.prio = prio
560 self.textadd(("prio:", self.prio, 0))
561
562configtypes.append(Suspended)
563
564class Iwait(StateEvent):
565 name = "iwait"
566 color = "grey"
567 enabled = 0
568 def __init__(self, thread, cpu, timestamp, prio):
569 StateEvent.__init__(self, thread, cpu, timestamp)
570 self.prio = prio
571 self.textadd(("prio:", self.prio, 0))
572
573configtypes.append(Iwait)
574
575class Preempted(StateEvent):
576 name = "preempted"
577 color = "red"
578 enabled = 1
579 def __init__(self, thread, cpu, timestamp, prio, bythread):
580 StateEvent.__init__(self, thread, cpu, timestamp)
581 self.skipnext = 1
582 self.prio = prio
583 self.linked = bythread
584 self.textadd(("prio:", self.prio, 0))
585 self.textadd(("by thread:", self.linked.name, 1))
586
587configtypes.append(Preempted)
588
589class Sleep(StateEvent):
590 name = "sleep"
591 color = "blue"
592 enabled = 1
593 def __init__(self, thread, cpu, timestamp, prio, wmesg):
594 StateEvent.__init__(self, thread, cpu, timestamp)
595 self.prio = prio
596 self.wmesg = wmesg
597 self.textadd(("prio:", self.prio, 0))
598 self.textadd(("wmesg:", self.wmesg, 0))
599
600 def stattxt(self):
601 statstr = StateEvent.stattxt(self)
602 statstr += " sleeping on: " + self.wmesg
603 return (statstr)
604
605configtypes.append(Sleep)
606
607class Blocked(StateEvent):
608 name = "blocked"
609 color = "dark red"
610 enabled = 1
611 def __init__(self, thread, cpu, timestamp, prio, lock):
612 StateEvent.__init__(self, thread, cpu, timestamp)
613 self.prio = prio
614 self.lock = lock
615 self.textadd(("prio:", self.prio, 0))
616 self.textadd(("lock:", self.lock, 0))
617
618 def stattxt(self):
619 statstr = StateEvent.stattxt(self)
620 statstr += " blocked on: " + self.lock
621 return (statstr)
622
623configtypes.append(Blocked)
624
625class KsegrpRunq(StateEvent):
626 name = "KsegrpRunq"
627 color = "orange"
628 enabled = 1
629 def __init__(self, thread, cpu, timestamp, prio, bythread):
630 StateEvent.__init__(self, thread, cpu, timestamp)
631 self.prio = prio
632 self.linked = bythread
633 self.textadd(("prio:", self.prio, 0))
634 self.textadd(("by thread:", self.linked.name, 1))
635
636configtypes.append(KsegrpRunq)
637
638class Runq(StateEvent):
639 name = "Runq"
640 color = "yellow"
641 enabled = 1
642 def __init__(self, thread, cpu, timestamp, prio, bythread):
643 StateEvent.__init__(self, thread, cpu, timestamp)
644 self.prio = prio
645 self.linked = bythread
646 self.textadd(("prio:", self.prio, 0))
647 self.textadd(("by thread:", self.linked.name, 1))
648
649configtypes.append(Runq)
650
651class Sched_exit_thread(StateEvent):
652 name = "exit_thread"
653 color = "grey"
654 enabled = 0
655 def __init__(self, thread, cpu, timestamp, prio):
656 StateEvent.__init__(self, thread, cpu, timestamp)
657 self.name = "sched_exit_thread"
658 self.prio = prio
659 self.textadd(("prio:", self.prio, 0))
660
661configtypes.append(Sched_exit_thread)
662
663class Sched_exit(StateEvent):
664 name = "exit"
665 color = "grey"
666 enabled = 0
667 def __init__(self, thread, cpu, timestamp, prio):
668 StateEvent.__init__(self, thread, cpu, timestamp)
669 self.name = "sched_exit"
670 self.prio = prio
671 self.textadd(("prio:", self.prio, 0))
672
673configtypes.append(Sched_exit)
674
675# Events for running callout routines
676
677class CalloutIdle(StateEvent):
678 name = "callwheel idle"
679 color = "grey"
680 enabled = 0
681 def __init__(self, wheel, cpu, timestamp):
682 StateEvent.__init__(self, wheel, cpu, timestamp)
683
684configtypes.append(CalloutIdle)
685
686class CalloutRunning(StateEvent):
687 name = "callout running"
688 color = "green"
689 enabled = 1
690 def __init__(self, wheel, cpu, timestamp, func, arg):
691 StateEvent.__init__(self, wheel, cpu, timestamp)
692 self.textadd(("function:", func, 0))
693 self.textadd(("argument:", arg, 0))
694 self.arg = arg
695 self.func = func
696
697 def stattxt(self):
698 statstr = StateEvent.stattxt(self)
699 statstr += " executing %s(%s)" % (self.func, self.arg)
700 return (statstr)
701
702configtypes.append(CalloutRunning)
703
704# Events on locks
705#
706# XXX: No support for upgrade/downgrade currently or differentiating
707# between read/write in general.
708#
709# XXX: Point events for recursion perhaps?
710
711class LockAcquire(StateEvent):
712 name = "lock acquire"
713 color = "blue"
714 enabled = 1
715 def __init__(self, lock, cpu, timestamp, file, line):
716 StateEvent.__init__(self, lock, cpu, timestamp)
717 self.textadd(("file:", file, 0))
718 self.textadd(("line:", line, 0))
719
720configtypes.append(LockAcquire)
721
722class LockContest(StateEvent):
723 name = "lock contest"
724 color = "purple"
725 enabled = 1
726 def __init__(self, lock, cpu, timestamp, file, line):
727 StateEvent.__init__(self, lock, cpu, timestamp)
728 self.textadd(("file:", file, 0))
729 self.textadd(("line:", line, 0))
730
731configtypes.append(LockContest)
732
733class LockFailedTry(PointEvent):
734 name = "failed lock try"
735 color = "red"
736 enabled = 1
737 def __init__(self, lock, cpu, timestamp, file, line):
738 PointEvent.__init__(self, lock, cpu, timestamp)
739 self.textadd(("file:", file, 0))
740 self.textadd(("line:", line, 0))
741
742configtypes.append(LockFailedTry)
743
744class LockRelease(StateEvent):
745 name = "lock release"
746 color = "grey"
747 enabled = 0
748 def __init__(self, lock, cpu, timestamp, file, line):
749 StateEvent.__init__(self, lock, cpu, timestamp)
750 self.textadd(("file:", file, 0))
751 self.textadd(("line:", line, 0))
752
753configtypes.append(LockRelease)
754
755class Padevent(StateEvent):
756 def __init__(self, thread, cpu, timestamp, last=0):
757 StateEvent.__init__(self, thread, cpu, timestamp, last)
758 self.name = "pad"
759 self.real = 0
760
539class PadEvent(StateEvent):
540 type = "pad"
541 def __init__(self, source, cpu, timestamp, last=0):
542 if (last):
543 cpu = source.events[len(source.events) -1].cpu
544 else:
545 cpu = source.events[0].cpu
546 StateEvent.__init__(self, source, "pad", cpu, timestamp, [])
761 def draw(self, canvas, xpos, ypos):
762 next = self.next()
763 if (next == None):
764 return (xpos)
547 def draw(self, canvas, xpos, ypos):
548 next = self.next()
549 if (next == None):
550 return (xpos)
765 self.duration = next.timestamp - self.timestamp
766 delta = self.duration / canvas.ratio
551 duration = next.timestamp - self.timestamp
552 delta = duration / canvas.ratio
553 Event.draw(self, canvas, xpos, ypos, None)
767 return (xpos + delta)
768
554 return (xpos + delta)
555
769class Tick(PointEvent):
770 name = "tick"
771 color = "black"
772 enabled = 0
773 def __init__(self, thread, cpu, timestamp, prio, stathz):
774 PointEvent.__init__(self, thread, cpu, timestamp)
775 self.prio = prio
776 self.textadd(("prio:", self.prio, 0))
777
778configtypes.append(Tick)
779
780class Prio(PointEvent):
781 name = "prio"
782 color = "black"
783 enabled = 0
784 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
785 PointEvent.__init__(self, thread, cpu, timestamp)
786 self.prio = prio
787 self.newprio = newprio
788 self.linked = bythread
789 self.textadd(("new prio:", self.newprio, 0))
790 self.textadd(("prio:", self.prio, 0))
791 if (self.linked != self.source):
792 self.textadd(("by thread:", self.linked.name, 1))
793 else:
794 self.textadd(("by thread:", self.linked.name, 0))
795
796configtypes.append(Prio)
797
798class Lend(PointEvent):
799 name = "lend"
800 color = "black"
801 enabled = 0
802 def __init__(self, thread, cpu, timestamp, prio, tothread):
803 PointEvent.__init__(self, thread, cpu, timestamp)
804 self.prio = prio
805 self.linked = tothread
806 self.textadd(("prio:", self.prio, 0))
807 self.textadd(("to thread:", self.linked.name, 1))
808
809configtypes.append(Lend)
810
811class Wokeup(PointEvent):
812 name = "wokeup"
813 color = "black"
814 enabled = 0
815 def __init__(self, thread, cpu, timestamp, ranthread):
816 PointEvent.__init__(self, thread, cpu, timestamp)
817 self.linked = ranthread
818 self.textadd(("ran thread:", self.linked.name, 1))
819
820configtypes.append(Wokeup)
821
822(DEFAULT, LOAD, COUNT, CALLWHEEL, LOCK, THREAD) = range(6)
823
824class EventSource:
556class EventSource:
825 def __init__(self, name, group=DEFAULT, order=0):
826 self.name = name
557 def __init__(self, group, id):
558 self.name = id
827 self.events = []
559 self.events = []
828 self.cpu = 0
829 self.cpux = 0
560 self.cpuitems = []
830 self.group = group
561 self.group = group
831 self.order = order
562 self.y = 0
563 self.item = None
832
833 def __cmp__(self, other):
564
565 def __cmp__(self, other):
566 if (other == None):
567 return -1
834 if (self.group == other.group):
568 if (self.group == other.group):
835 return cmp(self.order, other.order)
569 return cmp(self.name, other.name)
836 return cmp(self.group, other.group)
837
838 # It is much faster to append items to a list then to insert them
839 # at the beginning. As a result, we add events in reverse order
840 # and then swap the list during fixup.
841 def fixup(self):
842 self.events.reverse()
843
570 return cmp(self.group, other.group)
571
572 # It is much faster to append items to a list then to insert them
573 # at the beginning. As a result, we add events in reverse order
574 # and then swap the list during fixup.
575 def fixup(self):
576 self.events.reverse()
577
844 def event(self, event):
578 def addevent(self, event):
845 self.events.append(event)
846
579 self.events.append(event)
580
847 def remove(self, event):
848 self.events.remove(event)
849
850 def lastevent(self, event):
581 def addlastevent(self, event):
851 self.events.insert(0, event)
852
853 def draw(self, canvas, ypos):
854 xpos = 10
582 self.events.insert(0, event)
583
584 def draw(self, canvas, ypos):
585 xpos = 10
855 self.cpux = 10
856 self.cpu = self.events[1].cpu
586 cpux = 10
587 cpu = self.events[1].cpu
857 for i in range(0, len(self.events)):
858 self.events[i].idx = i
859 for event in self.events:
588 for i in range(0, len(self.events)):
589 self.events[i].idx = i
590 for event in self.events:
860 if (event.cpu != self.cpu and event.cpu != -1):
861 self.drawcpu(canvas, xpos, ypos)
862 self.cpux = xpos
863 self.cpu = event.cpu
591 if (event.cpu != cpu and event.cpu != -1):
592 self.drawcpu(canvas, cpu, cpux, xpos, ypos)
593 cpux = xpos
594 cpu = event.cpu
864 xpos = event.draw(canvas, xpos, ypos)
595 xpos = event.draw(canvas, xpos, ypos)
865 self.drawcpu(canvas, xpos, ypos)
596 self.drawcpu(canvas, cpu, cpux, xpos, ypos)
866
867 def drawname(self, canvas, ypos):
597
598 def drawname(self, canvas, ypos):
599 self.y = ypos
868 ypos = ypos - (self.ysize() / 2)
600 ypos = ypos - (self.ysize() / 2)
869 canvas.create_text(10, ypos, anchor="w", text=self.name)
601 self.item = canvas.create_text(10, ypos, anchor="w", text=self.name)
602 return (self.item)
870
603
871 def drawcpu(self, canvas, xpos, ypos):
872 cpu = int(self.cpu)
873 if (cpu == 0):
874 color = 'light grey'
875 elif (cpu == 1):
876 color = 'dark grey'
877 elif (cpu == 2):
878 color = 'light blue'
879 elif (cpu == 3):
880 color = 'light green'
881 elif (cpu == 4):
882 color = 'blanched almond'
883 elif (cpu == 5):
884 color = 'slate grey'
885 elif (cpu == 6):
886 color = 'light slate blue'
887 elif (cpu == 7):
888 color = 'thistle'
889 else:
890 color = "white"
891 l = canvas.create_rectangle(self.cpux,
604 def drawcpu(self, canvas, cpu, fromx, tox, ypos):
605 cpu = "CPU " + str(cpu)
606 color = cpucolormap.lookup(cpu)
607 # Create the cpu background colors default to hidden
608 l = canvas.create_rectangle(fromx,
892 ypos - self.ysize() - canvas.bdheight,
609 ypos - self.ysize() - canvas.bdheight,
893 xpos, ypos + canvas.bdheight, fill=color, width=0,
894 tags=("all", "cpuinfo"))
610 tox, ypos + canvas.bdheight, fill=color, width=0,
611 tags=("all", "cpuinfo", cpu), state="hidden")
612 self.cpuitems.append(l)
895
613
614 def move(self, canvas, xpos, ypos):
615 for event in self.events:
616 event.move(canvas, xpos, ypos)
617 for item in self.cpuitems:
618 canvas.move(item, xpos, ypos)
619
620 def movename(self, canvas, xpos, ypos):
621 self.y += ypos
622 canvas.move(self.item, xpos, ypos)
623
896 def ysize(self):
624 def ysize(self):
897 return (None)
625 return (10)
898
899 def eventat(self, i):
900 if (i >= len(self.events)):
901 return (None)
902 event = self.events[i]
903 return (event)
904
905 def findevent(self, timestamp):
906 for event in self.events:
626
627 def eventat(self, i):
628 if (i >= len(self.events)):
629 return (None)
630 event = self.events[i]
631 return (event)
632
633 def findevent(self, timestamp):
634 for event in self.events:
907 if (event.timestamp >= timestamp and event.real):
635 if (event.timestamp >= timestamp and event.type != "pad"):
908 return (event)
909 return (None)
910
636 return (event)
637 return (None)
638
911class Thread(EventSource):
912 names = {}
913 def __init__(self, td, pcomm):
914 EventSource.__init__(self, pcomm, THREAD)
915 self.str = td
639class Counter(EventSource):
640 #
641 # Store a hash of counter groups that keeps the max value
642 # for a counter in this group for scaling purposes.
643 #
644 groups = {}
645 def __init__(self, group, id):
916 try:
646 try:
917 cnt = Thread.names[pcomm]
647 Counter.cnt = Counter.groups[group]
918 except:
648 except:
919 Thread.names[pcomm] = 0
920 return
921 Thread.names[pcomm] = cnt + 1
649 Counter.groups[group] = 0
650 EventSource.__init__(self, group, id)
922
923 def fixup(self):
651
652 def fixup(self):
653 for event in self.events:
654 if (event.type != "count"):
655 continue;
656 count = int(event.count)
657 if (count > Counter.groups[self.group]):
658 Counter.groups[self.group] = count
924 EventSource.fixup(self)
659 EventSource.fixup(self)
925 cnt = Thread.names[self.name]
926 if (cnt == 0):
927 return
928 cnt -= 1
929 Thread.names[self.name] = cnt
930 self.name += " td" + str(cnt)
931
660
932 def ysize(self):
933 return (10)
934
935class Callwheel(EventSource):
936 count = 0
937 def __init__(self, cpu):
938 EventSource.__init__(self, "Callwheel", CALLWHEEL, cpu)
939 self.wheel = cpu
940 Callwheel.count += 1
941
942 def fixup(self):
943 EventSource.fixup(self)
944 if (Callwheel.count == 1):
945 return
946 self.name += " (CPU %d)" % (self.wheel)
947
948 def ysize(self):
949 return (10)
950
951class Lock(EventSource):
952 def __init__(self, lock):
953 EventSource.__init__(self, lock, LOCK)
954
955 def ysize(self):
956 return (10)
957
958class Counter(EventSource):
959 max = 0
960 def __init__(self, name):
961 EventSource.__init__(self, name, COUNT)
962
963 def event(self, event):
964 EventSource.event(self, event)
965 try:
966 count = event.count
967 except:
968 return
969 count = int(count)
970 if (count > Counter.max):
971 Counter.max = count
972
973 def ymax(self):
661 def ymax(self):
974 return (Counter.max)
662 return (Counter.groups[self.group])
975
976 def ysize(self):
977 return (80)
978
979 def yscale(self):
663
664 def ysize(self):
665 return (80)
666
667 def yscale(self):
980 return (self.ysize() / Counter.max)
668 return (self.ysize() / self.ymax())
981
669
982class CPULoad(Counter):
983 def __init__(self, cpu):
984 Counter.__init__(self, "cpu" + str(cpu) + " load")
985 self.group = LOAD
986 self.order = cpu
987
988class KTRFile:
989 def __init__(self, file):
990 self.timestamp_f = None
991 self.timestamp_l = None
670class KTRFile:
671 def __init__(self, file):
672 self.timestamp_f = None
673 self.timestamp_l = None
992 self.threads = []
993 self.sources = []
994 self.locks = {}
995 self.callwheels = {}
996 self.ticks = {}
997 self.load = {}
998 self.crit = {}
999 self.stathz = 0
1000
1001 self.parse(file)
1002 self.fixup()
1003 global ticksps
674 self.locks = {}
675 self.callwheels = {}
676 self.ticks = {}
677 self.load = {}
678 self.crit = {}
679 self.stathz = 0
680
681 self.parse(file)
682 self.fixup()
683 global ticksps
1004 print "first", self.timestamp_f, "last", self.timestamp_l
1005 print "time span", self.timespan()
1006 print "stathz", self.stathz
1007 ticksps = self.ticksps()
684 ticksps = self.ticksps()
685 timespan = self.timespan()
686 print "first tick", self.timestamp_f,
687 print "last tick", self.timestamp_l
1008 print "Ticks per second", ticksps
688 print "Ticks per second", ticksps
689 print "time span", timespan, "ticks", ticks2sec(timespan)
1009
1010 def parse(self, file):
1011 try:
1012 ifp = open(file)
1013 except:
1014 print "Can't open", file
1015 sys.exit(1)
1016
690
691 def parse(self, file):
692 try:
693 ifp = open(file)
694 except:
695 print "Can't open", file
696 sys.exit(1)
697
1017 ktrhdr = "\s*\d+\s+(\d+)\s+(\d+)\s+"
1018 tdname = "(\S+)\(([^)]*)\)"
1019 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
698 # quoteexp matches a quoted string, no escaping
699 quoteexp = "\"([^\"]*)\""
1020
700
1021# XXX doesn't handle:
1022# 371 0 61628682318 mi_switch: 0xc075c070(swapper) prio 180 inhibit 2 wmesg ATA request done lock (null)
1023 ktrstr = "mi_switch: " + tdname
1024 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
1025 switchout_re = re.compile(ktrhdr + ktrstr)
701 #
702 # commaexp matches a quoted string OR the string up
703 # to the first ','
704 #
705 commaexp = "(?:" + quoteexp + "|([^,]+))"
1026
706
1027 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
1028 idled_re = re.compile(ktrhdr + ktrstr)
707 #
708 # colonstr matches a quoted string OR the string up
709 # to the first ':'
710 #
711 colonexp = "(?:" + quoteexp + "|([^:]+))"
1029
712
1030 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
1031 ktrstr += tdname
1032 preempted_re = re.compile(ktrhdr + ktrstr)
713 #
714 # Match various manditory parts of the KTR string this is
715 # fairly inflexible until you get to attributes to make
716 # parsing faster.
717 #
718 hdrexp = "\s*(\d+)\s+(\d+)\s+(\d+)\s+"
719 groupexp = "KTRGRAPH group:" + quoteexp + ", "
720 idexp = "id:" + quoteexp + ", "
721 typeexp = "([^:]+):" + commaexp + ", "
722 attribexp = "attributes: (.*)"
1033
723
1034 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
1035 switchin_re = re.compile(ktrhdr + ktrstr)
724 #
725 # Matches optional attributes in the KTR string. This
726 # tolerates more variance as the users supply these values.
727 #
728 attrexp = colonexp + "\s*:\s*(?:" + commaexp + ", (.*)|"
729 attrexp += quoteexp +"|(.*))"
1036
730
1037 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
1038 sched_add_re = re.compile(ktrhdr + ktrstr)
731 # Precompile regexp
732 ktrre = re.compile(hdrexp + groupexp + idexp + typeexp + attribexp)
733 attrre = re.compile(attrexp)
1039
734
1040 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
1041 setrunqueue_re = re.compile(ktrhdr + ktrstr)
1042
1043 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
1044 sched_rem_re = re.compile(ktrhdr + ktrstr)
1045
1046 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
1047 sched_exit_thread_re = re.compile(ktrhdr + ktrstr)
1048
1049 ktrstr = "sched_exit: " + tdname + " prio (\d+)"
1050 sched_exit_re = re.compile(ktrhdr + ktrstr)
1051
1052 ktrstr = "statclock: " + tdname + " prio (\d+)"
1053 ktrstr += " stathz (\d+)"
1054 sched_clock_re = re.compile(ktrhdr + ktrstr)
1055
1056 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
1057 ktrstr += " newprio (\d+) by " + tdname
1058 sched_prio_re = re.compile(ktrhdr + ktrstr)
1059
1060 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
1061 cpuload2_re = re.compile(ktrhdr + "cpu (\d+) load: (\d+)")
1062 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
1063
1064 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
1065 critsec_re = re.compile(ktrhdr + ktrstr)
1066
1067 ktrstr = "callout 0x[a-f\d]+ "
1068 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1069 callout_start_re = re.compile(ktrhdr + ktrstr)
1070
1071 ktrstr = "callout mpsafe 0x[a-f\d]+ "
1072 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1073 callout_mpsafe_re = re.compile(ktrhdr + ktrstr)
1074
1075 ktrstr = "callout mtx 0x[a-f\d]+ "
1076 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1077 callout_mtx_re = re.compile(ktrhdr + ktrstr)
1078
1079 ktrstr = "callout 0x[a-f\d]+ finished"
1080 callout_stop_re = re.compile(ktrhdr + ktrstr)
1081
1082 ktrstr = "TRY_([RSWX]?LOCK) \(.*\) (.*) r = ([0-9]+)"
1083 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1084 lock_try_re = re.compile(ktrhdr + ktrstr)
1085
1086 ktrstr = "([RSWX]?UNLOCK) \(.*\) (.*) r = ([0-9]+)"
1087 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1088 lock_release_re = re.compile(ktrhdr + ktrstr)
1089
1090 ktrstr = "([RSWX]?LOCK) \(.*\) (.*) r = ([0-9]+)"
1091 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1092 lock_acquire_re = re.compile(ktrhdr + ktrstr)
1093
1094 ktrstr = "_mtx_lock_sleep: (.*) contested \(lock=0x?[0-9a-f]*\)"
1095 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1096 mtx_contested_re = re.compile(ktrhdr + ktrstr)
1097
1098 # XXX: Spin lock traces don't have lock name or file/line
1099
1100 ktrstr = "_rw_wlock_hard: (.*) contested \(lock=0x?[0-9a-f]*\)"
1101 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1102 rw_contested_re = re.compile(ktrhdr + ktrstr)
1103
1104 # XXX: Read lock traces for rwlocks contesting don't have
1105 # lock name or file/line
1106
1107 parsers = [[cpuload_re, self.cpuload],
1108 [cpuload2_re, self.cpuload2],
1109 [loadglobal_re, self.loadglobal],
1110 [switchin_re, self.switchin],
1111 [switchout_re, self.switchout],
1112 [sched_add_re, self.sched_add],
1113 [setrunqueue_re, self.sched_rem],
1114 [sched_prio_re, self.sched_prio],
1115 [preempted_re, self.preempted],
1116 [sched_rem_re, self.sched_rem],
1117 [sched_exit_thread_re, self.sched_exit_thread],
1118 [sched_exit_re, self.sched_exit],
1119 [sched_clock_re, self.sched_clock],
1120 [critsec_re, self.critsec],
1121 [callout_start_re, self.callout_start],
1122 [callout_mpsafe_re, self.callout_start],
1123 [callout_mtx_re, self.callout_start],
1124 [callout_stop_re, self.callout_stop],
1125 [lock_try_re, self.lock_try],
1126 [lock_release_re, self.lock_release],
1127 [lock_acquire_re, self.lock_acquire],
1128 [mtx_contested_re, self.lock_contest],
1129 [rw_contested_re, self.lock_contest],
1130 [idled_re, self.idled]]
1131
1132 global lineno
1133 lineno = 0
1134 for line in ifp.readlines():
1135 lineno += 1
735 global lineno
736 lineno = 0
737 for line in ifp.readlines():
738 lineno += 1
1136 if ((lineno % 1024) == 0):
739 if ((lineno % 2048) == 0):
1137 status.startup("Parsing line " + str(lineno))
740 status.startup("Parsing line " + str(lineno))
1138 for p in parsers:
1139 m = p[0].match(line)
1140 if (m != None):
1141 p[1](*m.groups())
1142 break
741 m = ktrre.match(line);
1143 if (m == None):
742 if (m == None):
1144 print line,
743 print "Can't parse", lineno, line,
744 continue;
745 (index, cpu, timestamp, group, id, type, dat, dat1, attrstring) = m.groups();
746 if (dat == None):
747 dat = dat1
748 if (self.checkstamp(timestamp) == 0):
749 print "Bad timestamp at", lineno, ":", line,
750 continue
751 #
752 # Build the table of optional attributes
753 #
754 attrs = []
755 while (attrstring != None):
756 m = attrre.match(attrstring.strip())
757 if (m == None):
758 break;
759 #
760 # Name may or may not be quoted.
761 #
762 # For val we have four cases:
763 # 1) quotes followed by comma and more
764 # attributes.
765 # 2) no quotes followed by comma and more
766 # attributes.
767 # 3) no more attributes or comma with quotes.
768 # 4) no more attributes or comma without quotes.
769 #
770 (name, name1, val, val1, attrstring, end, end1) = m.groups();
771 if (name == None):
772 name = name1
773 if (end == None):
774 end = end1
775 if (val == None):
776 val = val1
777 if (val == None):
778 val = end
779 if (name == "stathz"):
780 self.setstathz(val, cpu)
781 attrs.append((name, val))
782 args = (dat, cpu, timestamp, attrs)
783 e = self.makeevent(group, id, type, args)
784 if (e == None):
785 print "Unknown type", type, lineno, line,
1145
786
1146 def checkstamp(self, cpu, timestamp):
1147 timestamp = int(timestamp)
1148 if (self.timestamp_f == None):
1149 self.timestamp_f = timestamp;
1150 if (self.timestamp_l != None and timestamp > self.timestamp_l):
1151 return (0)
1152 self.timestamp_l = timestamp;
1153 return (timestamp)
787 def makeevent(self, group, id, type, args):
788 e = None
789 source = self.makeid(group, id, type)
790 if (type == "state"):
791 e = StateEvent(source, *args)
792 elif (type == "counter"):
793 e = CountEvent(source, *args)
794 elif (type == "point"):
795 e = PointEvent(source, *args)
796 if (e != None):
797 source.addevent(e);
798 return e
1154
799
1155 def timespan(self):
1156 return (self.timestamp_f - self.timestamp_l);
1157
1158 def ticksps(self):
1159 return (self.timespan() / self.ticks[0]) * int(self.stathz)
1160
1161 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
1162 TDI_SUSPENDED = 0x0001
1163 TDI_SLEEPING = 0x0002
1164 TDI_SWAPPED = 0x0004
1165 TDI_LOCK = 0x0008
1166 TDI_IWAIT = 0x0010
1167
1168 timestamp = self.checkstamp(cpu, timestamp)
1169 if (timestamp == 0):
1170 return
1171 inhibit = int(inhibit)
1172 thread = self.findtd(td, pcomm)
1173 if (inhibit & TDI_SWAPPED):
1174 Swapped(thread, cpu, timestamp, prio)
1175 elif (inhibit & TDI_SLEEPING):
1176 Sleep(thread, cpu, timestamp, prio, wmesg)
1177 elif (inhibit & TDI_LOCK):
1178 Blocked(thread, cpu, timestamp, prio, lock)
1179 elif (inhibit & TDI_IWAIT):
1180 Iwait(thread, cpu, timestamp, prio)
1181 elif (inhibit & TDI_SUSPENDED):
1182 Suspended(thread, cpu, timestamp, prio)
1183 elif (inhibit == 0):
1184 Yielding(thread, cpu, timestamp, prio)
1185 else:
1186 print "Unknown event", inhibit
1187 sys.exit(1)
1188
1189 def idled(self, cpu, timestamp, td, pcomm, prio):
1190 timestamp = self.checkstamp(cpu, timestamp)
1191 if (timestamp == 0):
1192 return
1193 thread = self.findtd(td, pcomm)
1194 Idle(thread, cpu, timestamp, prio)
1195
1196 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1197 timestamp = self.checkstamp(cpu, timestamp)
1198 if (timestamp == 0):
1199 return
1200 thread = self.findtd(td, pcomm)
1201 Preempted(thread, cpu, timestamp, prio,
1202 self.findtd(bytd, bypcomm))
1203
1204 def switchin(self, cpu, timestamp, td, pcomm, prio):
1205 timestamp = self.checkstamp(cpu, timestamp)
1206 if (timestamp == 0):
1207 return
1208 thread = self.findtd(td, pcomm)
1209 Running(thread, cpu, timestamp, prio)
1210
1211 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1212 timestamp = self.checkstamp(cpu, timestamp)
1213 if (timestamp == 0):
1214 return
1215 thread = self.findtd(td, pcomm)
1216 bythread = self.findtd(bytd, bypcomm)
1217 Runq(thread, cpu, timestamp, prio, bythread)
1218 Wokeup(bythread, cpu, timestamp, thread)
1219
1220 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1221 timestamp = self.checkstamp(cpu, timestamp)
1222 if (timestamp == 0):
1223 return
1224 thread = self.findtd(td, pcomm)
1225 KsegrpRunq(thread, cpu, timestamp, prio,
1226 self.findtd(bytd, bypcomm))
1227
1228 def sched_exit_thread(self, cpu, timestamp, td, pcomm, prio):
1229 timestamp = self.checkstamp(cpu, timestamp)
1230 if (timestamp == 0):
1231 return
1232 thread = self.findtd(td, pcomm)
1233 Sched_exit_thread(thread, cpu, timestamp, prio)
1234
1235 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1236 timestamp = self.checkstamp(cpu, timestamp)
1237 if (timestamp == 0):
1238 return
1239 thread = self.findtd(td, pcomm)
1240 Sched_exit(thread, cpu, timestamp, prio)
1241
1242 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1243 timestamp = self.checkstamp(cpu, timestamp)
1244 if (timestamp == 0):
1245 return
1246 self.stathz = stathz
800 def setstathz(self, val, cpu):
801 self.stathz = int(val)
1247 cpu = int(cpu)
1248 try:
1249 ticks = self.ticks[cpu]
1250 except:
1251 self.ticks[cpu] = 0
1252 self.ticks[cpu] += 1
802 cpu = int(cpu)
803 try:
804 ticks = self.ticks[cpu]
805 except:
806 self.ticks[cpu] = 0
807 self.ticks[cpu] += 1
1253 thread = self.findtd(td, pcomm)
1254 Tick(thread, cpu, timestamp, prio, stathz)
1255
808
1256 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1257 if (prio == newprio):
1258 return
1259 timestamp = self.checkstamp(cpu, timestamp)
1260 if (timestamp == 0):
1261 return
1262 thread = self.findtd(td, pcomm)
1263 bythread = self.findtd(bytd, bypcomm)
1264 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1265 Lend(bythread, cpu, timestamp, newprio, thread)
809 def checkstamp(self, timestamp):
810 timestamp = int(timestamp)
811 if (self.timestamp_f == None):
812 self.timestamp_f = timestamp;
813 if (self.timestamp_l != None and timestamp > self.timestamp_l):
814 return (0)
815 self.timestamp_l = timestamp;
816 return (1)
1266
817
1267 def cpuload(self, cpu, timestamp, count):
1268 timestamp = self.checkstamp(cpu, timestamp)
1269 if (timestamp == 0):
1270 return
1271 cpu = int(cpu)
1272 try:
1273 load = self.load[cpu]
1274 except:
1275 load = CPULoad(cpu)
1276 self.load[cpu] = load
1277 self.sources.append(load)
1278 Count(load, cpu, timestamp, count)
818 def makeid(self, group, id, type):
819 for source in sources:
820 if (source.name == id and source.group == group):
821 return source
822 if (type == "counter"):
823 source = Counter(group, id)
824 else:
825 source = EventSource(group, id)
826 sources.append(source)
827 return (source)
1279
828
1280 def cpuload2(self, cpu, timestamp, ncpu, count):
1281 timestamp = self.checkstamp(cpu, timestamp)
1282 if (timestamp == 0):
1283 return
1284 cpu = int(ncpu)
1285 try:
1286 load = self.load[cpu]
1287 except:
1288 load = CPULoad(cpu)
1289 self.load[cpu] = load
1290 self.sources.append(load)
1291 Count(load, cpu, timestamp, count)
829 def findid(self, id):
830 for source in sources:
831 if (source.name == id):
832 return source
833 return (None)
1292
834
1293 def loadglobal(self, cpu, timestamp, count):
1294 timestamp = self.checkstamp(cpu, timestamp)
1295 if (timestamp == 0):
1296 return
1297 cpu = 0
1298 try:
1299 load = self.load[cpu]
1300 except:
1301 load = Counter("CPU load")
1302 self.load[cpu] = load
1303 self.sources.append(load)
1304 Count(load, cpu, timestamp, count)
835 def timespan(self):
836 return (self.timestamp_f - self.timestamp_l);
1305
837
1306 def critsec(self, cpu, timestamp, td, pcomm, to):
1307 timestamp = self.checkstamp(cpu, timestamp)
1308 if (timestamp == 0):
1309 return
1310 cpu = int(cpu)
1311 try:
1312 crit = self.crit[cpu]
1313 except:
1314 crit = Counter("Critical Section")
1315 self.crit[cpu] = crit
1316 self.sources.append(crit)
1317 Count(crit, cpu, timestamp, to)
838 def ticksps(self):
839 oneghz = 1000000000
840 # Use user supplied clock first
841 if (clockfreq != None):
842 return int(clockfreq * oneghz)
1318
843
1319 def callout_start(self, cpu, timestamp, func, arg):
1320 timestamp = self.checkstamp(cpu, timestamp)
1321 if (timestamp == 0):
1322 return
1323 wheel = self.findwheel(cpu)
1324 CalloutRunning(wheel, cpu, timestamp, func, arg)
844 # Check for a discovered clock
845 if (self.stathz != None):
846 return (self.timespan() / self.ticks[0]) * int(self.stathz)
847 # Pretend we have a 1ns clock
848 print "WARNING: No clock discovered and no frequency ",
849 print "specified via the command line."
850 print "Using fake 1ghz clock"
851 return (oneghz);
1325
852
1326 def callout_stop(self, cpu, timestamp):
1327 timestamp = self.checkstamp(cpu, timestamp)
1328 if (timestamp == 0):
1329 return
1330 wheel = self.findwheel(cpu)
1331 CalloutIdle(wheel, cpu, timestamp)
853 def fixup(self):
854 for source in sources:
855 e = PadEvent(source, -1, self.timestamp_l)
856 source.addevent(e)
857 e = PadEvent(source, -1, self.timestamp_f, last=1)
858 source.addlastevent(e)
859 source.fixup()
860 sources.sort()
1332
861
1333 def lock_try(self, cpu, timestamp, op, name, result, file, line):
1334 timestamp = self.checkstamp(cpu, timestamp)
1335 if (timestamp == 0):
1336 return
1337 lock = self.findlock(name)
1338 if (int(result) == 0):
1339 LockFailedTry(lock, cpu, timestamp, file, line)
1340 else:
1341 LockAcquire(lock, cpu, timestamp, file, line)
862class SchedNames(Canvas):
863 def __init__(self, master, display):
864 self.display = display
865 self.parent = master
866 self.bdheight = master.bdheight
867 self.items = {}
868 self.ysize = 0
869 self.lines = []
870 Canvas.__init__(self, master, width=120,
871 height=display["height"], bg='grey',
872 scrollregion=(0, 0, 50, 100))
1342
873
1343 def lock_acquire(self, cpu, timestamp, op, name, recurse, file, line):
1344 if (int(recurse) != 0):
874 def moveline(self, cur_y, y):
875 for line in self.lines:
876 (x0, y0, x1, y1) = self.coords(line)
877 if (cur_y != y0):
878 continue
879 self.move(line, 0, y)
1345 return
880 return
1346 timestamp = self.checkstamp(cpu, timestamp)
1347 if (timestamp == 0):
1348 return
1349 lock = self.findlock(name)
1350 LockAcquire(lock, cpu, timestamp, file, line)
1351
1352 def lock_release(self, cpu, timestamp, op, name, recurse, file, line):
1353 if (int(recurse) != 0):
1354 return
1355 timestamp = self.checkstamp(cpu, timestamp)
1356 if (timestamp == 0):
1357 return
1358 lock = self.findlock(name)
1359 LockRelease(lock, cpu, timestamp, file, line)
1360
881
1361 def lock_contest(self, cpu, timestamp, name, file, line):
1362 timestamp = self.checkstamp(cpu, timestamp)
1363 if (timestamp == 0):
1364 return
1365 lock = self.findlock(name)
1366 LockContest(lock, cpu, timestamp, file, line)
882 def draw(self):
883 status.startup("Drawing names")
884 ypos = 0
885 self.configure(scrollregion=(0, 0,
886 self["width"], self.display.ysize()))
887 for source in sources:
888 l = self.create_line(0, ypos, self["width"], ypos,
889 width=1, fill="black", tags=("all","sources"))
890 self.lines.append(l)
891 ypos += self.bdheight
892 ypos += source.ysize()
893 t = source.drawname(self, ypos)
894 self.items[t] = source
895 ypos += self.bdheight
896 self.ysize = ypos
897 self.create_line(0, ypos, self["width"], ypos,
898 width=1, fill="black", tags=("all",))
899 self.bind("<Button-1>", self.master.mousepress);
900 self.bind("<ButtonRelease-1>", self.master.mouserelease);
901 self.bind("<B1-Motion>", self.master.mousemotion);
1367
902
1368 def findlock(self, name):
1369 try:
1370 lock = self.locks[name]
1371 except:
1372 lock = Lock(name)
1373 self.locks[name] = lock
1374 self.sources.append(lock)
1375 return (lock)
1376
903
1377 def findwheel(self, cpu):
1378 cpu = int(cpu)
1379 try:
1380 wheel = self.callwheels[cpu]
1381 except:
1382 wheel = Callwheel(cpu)
1383 self.callwheels[cpu] = wheel
1384 self.sources.append(wheel)
1385 return (wheel)
1386
1387 def findtd(self, td, pcomm):
1388 for thread in self.threads:
1389 if (thread.str == td and thread.name == pcomm):
1390 return thread
1391 thread = Thread(td, pcomm)
1392 self.threads.append(thread)
1393 self.sources.append(thread)
1394 return (thread)
1395
1396 def fixup(self):
1397 for source in self.sources:
1398 Padevent(source, -1, self.timestamp_l)
1399 Padevent(source, -1, self.timestamp_f, last=1)
1400 source.fixup()
1401 self.sources.sort()
1402
1403class SchedDisplay(Canvas):
1404 def __init__(self, master):
1405 self.ratio = 1
904class SchedDisplay(Canvas):
905 def __init__(self, master):
906 self.ratio = 1
1406 self.ktrfile = None
1407 self.sources = None
1408 self.parent = master
907 self.parent = master
1409 self.bdheight = 10
1410 self.events = {}
1411
908 self.bdheight = master.bdheight
909 self.items = {}
910 self.lines = []
1412 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1413 scrollregion=(0, 0, 800, 500))
1414
911 Canvas.__init__(self, master, width=800, height=500, bg='grey',
912 scrollregion=(0, 0, 800, 500))
913
1415 def setfile(self, ktrfile):
1416 self.ktrfile = ktrfile
1417 self.sources = ktrfile.sources
1418
914 def prepare(self):
915 #
1419 # Compute a ratio to ensure that the file's timespan fits into
1420 # 2^31. Although python may handle larger values for X
1421 # values, the Tk internals do not.
916 # Compute a ratio to ensure that the file's timespan fits into
917 # 2^31. Although python may handle larger values for X
918 # values, the Tk internals do not.
919 #
1422 self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1
1423
1424 def draw(self):
1425 ypos = 0
1426 xsize = self.xsize()
920 self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1
921
922 def draw(self):
923 ypos = 0
924 xsize = self.xsize()
1427 for source in self.sources:
925 for source in sources:
1428 status.startup("Drawing " + source.name)
926 status.startup("Drawing " + source.name)
1429 self.create_line(0, ypos, xsize, ypos,
927 l = self.create_line(0, ypos, xsize, ypos,
1430 width=1, fill="black", tags=("all",))
928 width=1, fill="black", tags=("all",))
929 self.lines.append(l)
1431 ypos += self.bdheight
1432 ypos += source.ysize()
1433 source.draw(self, ypos)
1434 ypos += self.bdheight
1435 try:
1436 self.tag_raise("point", "state")
1437 self.tag_lower("cpuinfo", "all")
1438 except:
1439 pass
1440 self.create_line(0, ypos, xsize, ypos,
1441 width=1, fill="black", tags=("all",))
1442 self.tag_bind("event", "<Enter>", self.mouseenter)
1443 self.tag_bind("event", "<Leave>", self.mouseexit)
930 ypos += self.bdheight
931 ypos += source.ysize()
932 source.draw(self, ypos)
933 ypos += self.bdheight
934 try:
935 self.tag_raise("point", "state")
936 self.tag_lower("cpuinfo", "all")
937 except:
938 pass
939 self.create_line(0, ypos, xsize, ypos,
940 width=1, fill="black", tags=("all",))
941 self.tag_bind("event", "<Enter>", self.mouseenter)
942 self.tag_bind("event", "<Leave>", self.mouseexit)
1444 self.tag_bind("event", "<Button-1>", self.mousepress)
943 self.bind("<Button-1>", self.mousepress)
1445 self.bind("<Button-4>", self.wheelup)
1446 self.bind("<Button-5>", self.wheeldown)
944 self.bind("<Button-4>", self.wheelup)
945 self.bind("<Button-5>", self.wheeldown)
946 self.bind("<ButtonRelease-1>", self.master.mouserelease);
947 self.bind("<B1-Motion>", self.master.mousemotion);
1447
948
949 def moveline(self, cur_y, y):
950 for line in self.lines:
951 (x0, y0, x1, y1) = self.coords(line)
952 if (cur_y != y0):
953 continue
954 self.move(line, 0, y)
955 return
956
1448 def mouseenter(self, event):
1449 item, = self.find_withtag(CURRENT)
957 def mouseenter(self, event):
958 item, = self.find_withtag(CURRENT)
1450 event = self.events[item]
1451 event.mouseenter(self, item)
959 self.items[item].mouseenter(self)
1452
1453 def mouseexit(self, event):
1454 item, = self.find_withtag(CURRENT)
960
961 def mouseexit(self, event):
962 item, = self.find_withtag(CURRENT)
1455 event = self.events[item]
1456 event.mouseexit(self, item)
963 self.items[item].mouseexit(self)
1457
1458 def mousepress(self, event):
964
965 def mousepress(self, event):
1459 item, = self.find_withtag(CURRENT)
1460 event = self.events[item]
1461 event.mousepress(self, item)
966 # Find out what's beneath us
967 items = self.find_withtag(CURRENT)
968 if (len(items) == 0):
969 self.master.mousepress(event)
970 return
971 # Only grab mouse presses for things with event tags.
972 item = items[0]
973 tags = self.gettags(item)
974 for tag in tags:
975 if (tag == "event"):
976 self.items[item].mousepress(self)
977 return
978 # Leave the rest to the master window
979 self.master.mousepress(event)
1462
1463 def wheeldown(self, event):
1464 self.parent.display_yview("scroll", 1, "units")
1465
1466 def wheelup(self, event):
1467 self.parent.display_yview("scroll", -1, "units")
1468
980
981 def wheeldown(self, event):
982 self.parent.display_yview("scroll", 1, "units")
983
984 def wheelup(self, event):
985 self.parent.display_yview("scroll", -1, "units")
986
1469 def drawnames(self, canvas):
1470 status.startup("Drawing names")
1471 ypos = 0
1472 canvas.configure(scrollregion=(0, 0,
1473 canvas["width"], self.ysize()))
1474 for source in self.sources:
1475 canvas.create_line(0, ypos, canvas["width"], ypos,
1476 width=1, fill="black", tags=("all",))
1477 ypos += self.bdheight
1478 ypos += source.ysize()
1479 source.drawname(canvas, ypos)
1480 ypos += self.bdheight
1481 canvas.create_line(0, ypos, canvas["width"], ypos,
1482 width=1, fill="black", tags=("all",))
1483
1484 def xsize(self):
987 def xsize(self):
1485 return ((self.ktrfile.timespan() / self.ratio) + 20)
988 return ((ktrfile.timespan() / self.ratio) + 20)
1486
1487 def ysize(self):
1488 ysize = 0
989
990 def ysize(self):
991 ysize = 0
1489 for source in self.sources:
992 for source in sources:
1490 ysize += source.ysize() + (self.bdheight * 2)
993 ysize += source.ysize() + (self.bdheight * 2)
1491 return (ysize)
994 return ysize
1492
1493 def scaleset(self, ratio):
995
996 def scaleset(self, ratio):
1494 if (self.ktrfile == None):
997 if (ktrfile == None):
1495 return
1496 oldratio = self.ratio
998 return
999 oldratio = self.ratio
1497 xstart, ystart = self.xview()
1498 length = (float(self["width"]) / self.xsize())
1499 middle = xstart + (length / 2)
1000 xstart, xend = self.xview()
1001 midpoint = xstart + ((xend - xstart) / 2)
1500
1501 self.ratio = ratio
1502 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1503 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1504
1002
1003 self.ratio = ratio
1004 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1005 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1006
1505 length = (float(self["width"]) / self.xsize())
1506 xstart = middle - (length / 2)
1507 self.xview_moveto(xstart)
1007 xstart, xend = self.xview()
1008 xsize = (xend - xstart) / 2
1009 self.xview_moveto(midpoint - xsize)
1508
1509 def scaleget(self):
1510 return self.ratio
1511
1010
1011 def scaleget(self):
1012 return self.ratio
1013
1014 def getcolor(self, tag):
1015 return self.itemcget(tag, "fill")
1016
1017 def getstate(self, tag):
1018 return self.itemcget(tag, "state")
1019
1512 def setcolor(self, tag, color):
1513 self.itemconfigure(tag, state="normal", fill=color)
1514
1515 def hide(self, tag):
1516 self.itemconfigure(tag, state="hidden")
1517
1518class GraphMenu(Frame):
1519 def __init__(self, master):
1520 Frame.__init__(self, master, bd=2, relief=RAISED)
1521 self.view = Menubutton(self, text="Configure")
1522 self.viewmenu = Menu(self.view, tearoff=0)
1020 def setcolor(self, tag, color):
1021 self.itemconfigure(tag, state="normal", fill=color)
1022
1023 def hide(self, tag):
1024 self.itemconfigure(tag, state="hidden")
1025
1026class GraphMenu(Frame):
1027 def __init__(self, master):
1028 Frame.__init__(self, master, bd=2, relief=RAISED)
1029 self.view = Menubutton(self, text="Configure")
1030 self.viewmenu = Menu(self.view, tearoff=0)
1523 self.viewmenu.add_command(label="Events",
1031 self.viewmenu.add_command(label="Event Colors",
1524 command=self.econf)
1032 command=self.econf)
1033 self.viewmenu.add_command(label="CPU Colors",
1034 command=self.cconf)
1525 self.view["menu"] = self.viewmenu
1526 self.view.pack(side=LEFT)
1527
1528 def econf(self):
1035 self.view["menu"] = self.viewmenu
1036 self.view.pack(side=LEFT)
1037
1038 def econf(self):
1529 EventConfigure()
1039 ColorConfigure(eventcolors, "Event Display Configuration")
1530
1040
1041 def cconf(self):
1042 ColorConfigure(cpucolors, "CPU Background Colors")
1531
1043
1044
1532class SchedGraph(Frame):
1533 def __init__(self, master):
1534 Frame.__init__(self, master)
1535 self.menu = None
1536 self.names = None
1537 self.display = None
1538 self.scale = None
1539 self.status = None
1045class SchedGraph(Frame):
1046 def __init__(self, master):
1047 Frame.__init__(self, master)
1048 self.menu = None
1049 self.names = None
1050 self.display = None
1051 self.scale = None
1052 self.status = None
1053 self.bdheight = 10
1054 self.clicksource = None
1055 self.lastsource = None
1540 self.pack(expand=1, fill="both")
1541 self.buildwidgets()
1542 self.layout()
1056 self.pack(expand=1, fill="both")
1057 self.buildwidgets()
1058 self.layout()
1543 self.draw(sys.argv[1])
1544
1545 def buildwidgets(self):
1546 global status
1547 self.menu = GraphMenu(self)
1548 self.display = SchedDisplay(self)
1059
1060 def buildwidgets(self):
1061 global status
1062 self.menu = GraphMenu(self)
1063 self.display = SchedDisplay(self)
1549 self.names = Canvas(self,
1550 width=120, height=self.display["height"],
1551 bg='grey', scrollregion=(0, 0, 50, 100))
1064 self.names = SchedNames(self, self.display)
1552 self.scale = Scaler(self, self.display)
1553 status = self.status = Status(self)
1554 self.scrollY = Scrollbar(self, orient="vertical",
1555 command=self.display_yview)
1556 self.display.scrollX = Scrollbar(self, orient="horizontal",
1557 command=self.display.xview)
1558 self.display["xscrollcommand"] = self.display.scrollX.set
1559 self.display["yscrollcommand"] = self.scrollY.set

--- 6 unchanged lines hidden (view full) ---

1566 self.names.grid(row=1, column=0, sticky=N+S)
1567 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1568 self.scrollY.grid(row=1, column=2, sticky=N+S)
1569 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1570 sticky=E+W)
1571 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1572 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1573
1065 self.scale = Scaler(self, self.display)
1066 status = self.status = Status(self)
1067 self.scrollY = Scrollbar(self, orient="vertical",
1068 command=self.display_yview)
1069 self.display.scrollX = Scrollbar(self, orient="horizontal",
1070 command=self.display.xview)
1071 self.display["xscrollcommand"] = self.display.scrollX.set
1072 self.display["yscrollcommand"] = self.scrollY.set

--- 6 unchanged lines hidden (view full) ---

1079 self.names.grid(row=1, column=0, sticky=N+S)
1080 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1081 self.scrollY.grid(row=1, column=2, sticky=N+S)
1082 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1083 sticky=E+W)
1084 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1085 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1086
1574 def draw(self, file):
1087 def draw(self):
1575 self.master.update()
1088 self.master.update()
1576 ktrfile = KTRFile(file)
1577 self.display.setfile(ktrfile)
1578 self.display.drawnames(self.names)
1089 self.display.prepare()
1090 self.names.draw()
1579 self.display.draw()
1091 self.display.draw()
1092 self.status.startup("")
1580 self.scale.set(250000)
1581 self.display.xview_moveto(0)
1582
1093 self.scale.set(250000)
1094 self.display.xview_moveto(0)
1095
1096 def mousepress(self, event):
1097 self.clicksource = self.sourceat(event.y)
1098
1099 def mouserelease(self, event):
1100 if (self.clicksource == None):
1101 return
1102 newsource = self.sourceat(event.y)
1103 if (self.clicksource != newsource):
1104 self.sourceswap(self.clicksource, newsource)
1105 self.clicksource = None
1106 self.lastsource = None
1107
1108 def mousemotion(self, event):
1109 if (self.clicksource == None):
1110 return
1111 newsource = self.sourceat(event.y)
1112 #
1113 # If we get a None source they moved off the page.
1114 # swapsource() can't handle moving multiple items so just
1115 # pretend we never clicked on anything to begin with so the
1116 # user can't mouseover a non-contiguous area.
1117 #
1118 if (newsource == None):
1119 self.clicksource = None
1120 self.lastsource = None
1121 return
1122 if (newsource == self.lastsource):
1123 return;
1124 self.lastsource = newsource
1125 if (newsource != self.clicksource):
1126 self.sourceswap(self.clicksource, newsource)
1127
1128 # These are here because this object controls layout
1129 def sourcestart(self, source):
1130 return source.y - self.bdheight - source.ysize()
1131
1132 def sourceend(self, source):
1133 return source.y + self.bdheight
1134
1135 def sourcesize(self, source):
1136 return (self.bdheight * 2) + source.ysize()
1137
1138 def sourceswap(self, source1, source2):
1139 # Sort so we always know which one is on top.
1140 if (source2.y < source1.y):
1141 swap = source1
1142 source1 = source2
1143 source2 = swap
1144 # Only swap adjacent sources
1145 if (self.sourceend(source1) != self.sourcestart(source2)):
1146 return
1147 # Compute start coordinates and target coordinates
1148 y1 = self.sourcestart(source1)
1149 y2 = self.sourcestart(source2)
1150 y1targ = y1 + self.sourcesize(source2)
1151 y2targ = y1
1152 #
1153 # If the sizes are not equal, adjust the start of the lower
1154 # source to account for the lost/gained space.
1155 #
1156 if (source1.ysize() != source2.ysize()):
1157 diff = source2.ysize() - source1.ysize()
1158 self.names.moveline(y2, diff);
1159 self.display.moveline(y2, diff)
1160 source1.move(self.display, 0, y1targ - y1)
1161 source2.move(self.display, 0, y2targ - y2)
1162 source1.movename(self.names, 0, y1targ - y1)
1163 source2.movename(self.names, 0, y2targ - y2)
1164
1165 def sourceat(self, ypos):
1166 (start, end) = self.names.yview()
1167 starty = start * float(self.names.ysize)
1168 ypos += starty
1169 for source in sources:
1170 yend = self.sourceend(source)
1171 ystart = self.sourcestart(source)
1172 if (ypos >= ystart and ypos <= yend):
1173 return source
1174 return None
1175
1583 def display_yview(self, *args):
1584 self.names.yview(*args)
1585 self.display.yview(*args)
1586
1587 def setcolor(self, tag, color):
1588 self.display.setcolor(tag, color)
1589
1590 def hide(self, tag):
1591 self.display.hide(tag)
1592
1176 def display_yview(self, *args):
1177 self.names.yview(*args)
1178 self.display.yview(*args)
1179
1180 def setcolor(self, tag, color):
1181 self.display.setcolor(tag, color)
1182
1183 def hide(self, tag):
1184 self.display.hide(tag)
1185
1593if (len(sys.argv) != 2):
1594 print "usage:", sys.argv[0], "<ktr file>"
1186 def getcolor(self, tag):
1187 return self.display.getcolor(tag)
1188
1189 def getstate(self, tag):
1190 return self.display.getstate(tag)
1191
1192if (len(sys.argv) != 2 and len(sys.argv) != 3):
1193 print "usage:", sys.argv[0], "<ktr file> [clock freq in ghz]"
1595 sys.exit(1)
1596
1194 sys.exit(1)
1195
1196if (len(sys.argv) > 2):
1197 clockfreq = float(sys.argv[2])
1198
1597root = Tk()
1598root.title("Scheduler Graph")
1199root = Tk()
1200root.title("Scheduler Graph")
1201colormap = Colormap(eventcolors)
1202cpucolormap = Colormap(cpucolors)
1599graph = SchedGraph(root)
1203graph = SchedGraph(root)
1204ktrfile = KTRFile(sys.argv[1])
1205graph.draw()
1600root.mainloop()
1206root.mainloop()