Deleted Added
full compact
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
31import random
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'
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
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
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
122ticksps = None
123status = None
77configtypes = []
124colormap = None
125ktrfile = None
126clockfreq = None
127sources = []
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
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):
196 Frame.__init__(self, master)
197 if (graph.getstate(name) == "hidden"):
198 enabled = 0
199 else:
200 enabled = 1
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)
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)
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):
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):
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")
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)
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)
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",
283 command=self.rpress)
223 self.default = Button(self.buttons, text="Default",
224 command=self.dpress)
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)
286 self.buttons.columnconfigure(0, weight=1)
287 self.buttons.columnconfigure(1, weight=1)
230 self.buttons.columnconfigure(2, weight=1)
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
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)
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)
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(("", ""))
329 for label in labels:
277 name, value, linked = label
330 name, value = label
331 linked = 0
332 if (name == "linkedto"):
333 linked = 1
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"):
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"):
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):
395 self.source = source
396 self.name = name
397 self.cpu = cpu
398 self.timestamp = int(timestamp)
344 self.entries = []
345 self.real = 1
399 self.attrs = attrs
400 self.idx = None
347 self.state = 0
401 self.item = None
402 self.dispcnt = 0
350 self.linked = None
403 self.recno = lineno
352 if (last):
353 source.lastevent(self)
354 else:
355 source.event(self)
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 += ", "
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
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):
426 self.displayref(canvas)
427 self.status()
428
382 def mouseexit(self, canvas, item):
429 def mouseexit(self, canvas):
430 self.displayunref(canvas)
431 status.clear()
432
386 def mousepress(self, canvas, item):
433 def mousepress(self, canvas):
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
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
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
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)
481
482 def draw(self, canvas, xpos, ypos):
483 color = colormap.lookup(self.name)
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)
488
422 return (xpos)
489 return xpos
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)
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):
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
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
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)
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)
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
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
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)
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, [])
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)
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
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
559 self.events = []
828 self.cpu = 0
829 self.cpux = 0
560 self.cpuitems = []
561 self.group = group
831 self.order = order
562 self.y = 0
563 self.item = None
564
565 def __cmp__(self, other):
566 if (other == None):
567 return -1
568 if (self.group == other.group):
835 return cmp(self.order, other.order)
569 return cmp(self.name, other.name)
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):
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):
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
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
595 xpos = event.draw(canvas, xpos, ypos)
865 self.drawcpu(canvas, xpos, ypos)
596 self.drawcpu(canvas, cpu, cpux, xpos, ypos)
597
598 def drawname(self, canvas, ypos):
599 self.y = ypos
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)
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,
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)
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
624 def ysize(self):
897 return (None)
625 return (10)
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"):
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):
646 try:
917 cnt = Thread.names[pcomm]
647 Counter.cnt = Counter.groups[group]
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)
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
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)
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
661 def ymax(self):
974 return (Counter.max)
662 return (Counter.groups[self.group])
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())
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
670class KTRFile:
671 def __init__(self, file):
672 self.timestamp_f = None
673 self.timestamp_l = None
992 self.threads = []
993 self.sources = []
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
684 ticksps = self.ticksps()
685 timespan = self.timespan()
686 print "first tick", self.timestamp_f,
687 print "last tick", self.timestamp_l
688 print "Ticks per second", ticksps
689 print "time span", timespan, "ticks", ticks2sec(timespan)
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 = "\"([^\"]*)\""
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 + "|([^,]+))"
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 + "|([^:]+))"
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: (.*)"
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 +"|(.*))"
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)
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
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):
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);
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,
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
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)
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)
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)
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)
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)
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);
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)
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);
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()
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))
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)
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)
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);
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)
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
904class SchedDisplay(Canvas):
905 def __init__(self, master):
906 self.ratio = 1
1406 self.ktrfile = None
1407 self.sources = None
907 self.parent = master
1409 self.bdheight = 10
1410 self.events = {}
1411
908 self.bdheight = master.bdheight
909 self.items = {}
910 self.lines = []
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 #
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 #
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:
926 status.startup("Drawing " + source.name)
1429 self.create_line(0, ypos, xsize, ypos,
927 l = self.create_line(0, ypos, xsize, ypos,
928 width=1, fill="black", tags=("all",))
929 self.lines.append(l)
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)
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);
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
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)
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)
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)
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
987 def xsize(self):
1485 return ((self.ktrfile.timespan() / self.ratio) + 20)
988 return ((ktrfile.timespan() / self.ratio) + 20)
989
990 def ysize(self):
991 ysize = 0
1489 for source in self.sources:
992 for source in sources:
993 ysize += source.ysize() + (self.bdheight * 2)
1491 return (ysize)
994 return ysize
995
996 def scaleset(self, ratio):
1494 if (self.ktrfile == None):
997 if (ktrfile == None):
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)
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)
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
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",
1032 command=self.econf)
1033 self.viewmenu.add_command(label="CPU Colors",
1034 command=self.cconf)
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")
1040
1041 def cconf(self):
1042 ColorConfigure(cpucolors, "CPU Background Colors")
1043
1044
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
1056 self.pack(expand=1, fill="both")
1057 self.buildwidgets()
1058 self.layout()
1543 self.draw(sys.argv[1])
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)
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):
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()
1091 self.display.draw()
1092 self.status.startup("")
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
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]"
1194 sys.exit(1)
1195
1196if (len(sys.argv) > 2):
1197 clockfreq = float(sys.argv[2])
1198
1199root = Tk()
1200root.title("Scheduler Graph")
1201colormap = Colormap(eventcolors)
1202cpucolormap = Colormap(cpucolors)
1203graph = SchedGraph(root)
1204ktrfile = KTRFile(sys.argv[1])
1205graph.draw()
1206root.mainloop()