]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/sched/schedgraph.py
This commit was generated by cvs2svn to compensate for changes in r168988,
[FreeBSD/FreeBSD.git] / tools / sched / schedgraph.py
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:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice unmodified, this list of conditions, and the following
11 #    disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #     documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
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$
28
29 import sys
30 import re
31 from Tkinter import *
32
33 # To use:
34 # - Install the ports/x11-toolkits/py-tkinter package.
35 # - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF
36 # - It is encouraged to increase KTR_ENTRIES size to 32768 to gather
37 #    enough information for analysis.
38 # - Rebuild kernel with proper changes to KERNCONF and boot new kernel.
39 # - Run your workload to be profiled.
40 # - While the workload is continuing (i.e. before it finishes), disable
41 #   KTR tracing by setting 'sysctl debug.ktr.mask=0'.  This is necessary
42 #   to avoid a race condition while running ktrdump, i.e. the KTR ring buffer
43 #   will cycle a bit while ktrdump runs, and this confuses schedgraph because
44 #   the timestamps appear to go backwards at some point.  Stopping KTR logging
45 #   while the workload is still running is to avoid wasting log entries on
46 #   "idle" time at the end.
47 # - Dump the trace to a file: 'ktrdump -ct > ktr.out'
48 # - Run the python script: 'python schedgraph.py ktr.out'
49 #
50 # To do:
51 # 1)  Add a per-thread summary display
52 # 2)  Add bounding box style zoom.
53 # 3)  Click to center.
54 # 4)  Implement some sorting mechanism.
55 #
56 # BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of
57 #          colours to represent them ;-)
58 #       2) Extremely short traces may cause a crash because the code
59 #          assumes there is always at least one stathz entry logged, and
60 #          the number of such events is used as a denominator
61
62 ticksps = None
63 status = None
64 configtypes = []
65
66 def ticks2sec(ticks):
67         us = ticksps / 1000000
68         ticks /= us
69         if (ticks < 1000):
70                 return (str(ticks) + "us")
71         ticks /= 1000
72         if (ticks < 1000):
73                 return (str(ticks) + "ms")
74         ticks /= 1000
75         return (str(ticks) + "s")
76
77 class Scaler(Frame):
78         def __init__(self, master, target):
79                 Frame.__init__(self, master)
80                 self.scale = Scale(self, command=self.scaleset,
81                     from_=1000, to_=10000000, orient=HORIZONTAL,
82                     resolution=1000)
83                 self.label = Label(self, text="Ticks per pixel")
84                 self.label.pack(side=LEFT)
85                 self.scale.pack(fill="both", expand=1)
86                 self.target = target
87                 self.scale.set(target.scaleget())
88                 self.initialized = 1
89
90         def scaleset(self, value):
91                 self.target.scaleset(int(value))
92
93         def set(self, value):
94                 self.scale.set(value)
95
96 class Status(Frame):
97         def __init__(self, master):
98                 Frame.__init__(self, master)
99                 self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
100                 self.label.pack(fill="both", expand=1)
101                 self.clear()
102
103         def set(self, str):
104                 self.label.config(text=str)
105
106         def clear(self):
107                 self.label.config(text="")
108
109         def startup(self, str):
110                 self.set(str)
111                 root.update()
112
113 class EventConf(Frame):
114         def __init__(self, master, name, color, enabled):
115                 Frame.__init__(self, master)
116                 self.name = name
117                 self.color = StringVar()
118                 self.color_default = color
119                 self.color_current = color
120                 self.color.set(color)
121                 self.enabled = IntVar()
122                 self.enabled_default = enabled
123                 self.enabled_current = enabled
124                 self.enabled.set(enabled)
125                 self.draw()
126
127         def draw(self):
128                 self.label = Label(self, text=self.name, anchor=W)
129                 self.sample = Canvas(self, width=24, height=24,
130                     bg='grey')
131                 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
132                     fill=self.color.get())
133                 self.list = OptionMenu(self, self.color,
134                     "dark red", "red", "pink",
135                     "dark orange", "orange",
136                     "yellow", "light yellow",
137                     "dark green", "green", "light green",
138                     "dark blue", "blue", "light blue",
139                     "dark violet", "violet", "purple",
140                     "dark grey", "light grey",
141                     "white", "black",
142                     command=self.setcolor)
143                 self.checkbox = Checkbutton(self, text="enabled",
144                     variable=self.enabled)
145                 self.label.grid(row=0, column=0, sticky=E+W)
146                 self.sample.grid(row=0, column=1)
147                 self.list.grid(row=0, column=2, sticky=E+W)
148                 self.checkbox.grid(row=0, column=3)
149                 self.columnconfigure(0, weight=1)
150                 self.columnconfigure(2, minsize=110)
151
152         def setcolor(self, color):
153                 self.color.set(color)
154                 self.sample.itemconfigure(self.rect, fill=color)
155
156         def apply(self):
157                 cchange = 0
158                 echange = 0
159                 if (self.color_current != self.color.get()):
160                         cchange = 1
161                 if (self.enabled_current != self.enabled.get()):
162                         echange = 1
163                 self.color_current = self.color.get()
164                 self.enabled_current = self.enabled.get()
165                 if (echange != 0):
166                         if (self.enabled_current):
167                                 graph.setcolor(self.name, self.color_current)
168                         else:
169                                 graph.hide(self.name)
170                         return
171                 if (cchange != 0):
172                         graph.setcolor(self.name, self.color_current)
173
174         def revert(self):
175                 self.setcolor(self.color_current)
176                 self.enabled.set(self.enabled_current)
177
178         def default(self):
179                 self.setcolor(self.color_default)
180                 self.enabled.set(self.enabled_default)
181
182 class EventConfigure(Toplevel):
183         def __init__(self):
184                 Toplevel.__init__(self)
185                 self.resizable(0, 0)
186                 self.title("Event Configuration")
187                 self.items = LabelFrame(self, text="Event Type")
188                 self.buttons = Frame(self)
189                 self.drawbuttons()
190                 self.items.grid(row=0, column=0, sticky=E+W)
191                 self.columnconfigure(0, weight=1)
192                 self.buttons.grid(row=1, column=0, sticky=E+W)
193                 self.types = []
194                 self.irow = 0
195                 for type in configtypes:
196                         self.additem(type.name, type.color, type.enabled)
197
198         def additem(self, name, color, enabled=1):
199                 item = EventConf(self.items, name, color, enabled)
200                 self.types.append(item)
201                 item.grid(row=self.irow, column=0, sticky=E+W)
202                 self.irow += 1
203
204         def drawbuttons(self):
205                 self.apply = Button(self.buttons, text="Apply",
206                     command=self.apress)
207                 self.revert = Button(self.buttons, text="Revert",
208                     command=self.rpress)
209                 self.default = Button(self.buttons, text="Default",
210                     command=self.dpress)
211                 self.apply.grid(row=0, column=0, sticky=E+W)
212                 self.revert.grid(row=0, column=1, sticky=E+W)
213                 self.default.grid(row=0, column=2, sticky=E+W)
214                 self.buttons.columnconfigure(0, weight=1)
215                 self.buttons.columnconfigure(1, weight=1)
216                 self.buttons.columnconfigure(2, weight=1)
217
218         def apress(self):
219                 for item in self.types:
220                         item.apply()
221
222         def rpress(self):
223                 for item in self.types:
224                         item.revert()
225
226         def dpress(self):
227                 for item in self.types:
228                         item.default()
229
230 class EventView(Toplevel):
231         def __init__(self, event, canvas):
232                 Toplevel.__init__(self)
233                 self.resizable(0, 0)
234                 self.title("Event")
235                 self.event = event
236                 self.frame = Frame(self)
237                 self.frame.grid(row=0, column=0, sticky=N+S+E+W)
238                 self.buttons = Frame(self)
239                 self.buttons.grid(row=1, column=0, sticky=E+W)
240                 self.canvas = canvas
241                 self.drawlabels()
242                 self.drawbuttons()
243                 event.displayref(canvas)
244                 self.bind("<Destroy>", self.destroycb)
245
246         def destroycb(self, event):
247                 self.unbind("<Destroy>")
248                 if (self.event != None):
249                         self.event.displayunref(self.canvas)
250                         self.event = None
251                 self.destroy()
252
253         def clearlabels(self):
254                 for label in self.frame.grid_slaves():
255                         label.grid_remove()
256
257         def drawlabels(self):
258                 ypos = 0
259                 labels = self.event.labels()
260                 while (len(labels) < 7):
261                         labels.append(("", "", 0))
262                 for label in labels:
263                         name, value, linked = label
264                         l = Label(self.frame, text=name, bd=1, width=15,
265                             relief=SUNKEN, anchor=W)
266                         if (linked):
267                                 fgcolor = "blue"
268                         else:
269                                 fgcolor = "black"
270                         r = Label(self.frame, text=value, bd=1,
271                             relief=SUNKEN, anchor=W, fg=fgcolor)
272                         l.grid(row=ypos, column=0, sticky=E+W)
273                         r.grid(row=ypos, column=1, sticky=E+W)
274                         if (linked):
275                                 r.bind("<Button-1>", self.linkpress)
276                         ypos += 1
277                 self.frame.columnconfigure(1, minsize=80)
278
279         def drawbuttons(self):
280                 self.back = Button(self.buttons, text="<", command=self.bpress)
281                 self.forw = Button(self.buttons, text=">", command=self.fpress)
282                 self.new = Button(self.buttons, text="new", command=self.npress)
283                 self.back.grid(row=0, column=0, sticky=E+W)
284                 self.forw.grid(row=0, column=1, sticky=E+W)
285                 self.new.grid(row=0, column=2, sticky=E+W)
286                 self.buttons.columnconfigure(2, weight=1)
287
288         def newevent(self, event):
289                 self.event.displayunref(self.canvas)
290                 self.clearlabels()
291                 self.event = event
292                 self.event.displayref(self.canvas)
293                 self.drawlabels()
294
295         def npress(self):
296                 EventView(self.event, self.canvas)
297
298         def bpress(self):
299                 prev = self.event.prev()
300                 if (prev == None):
301                         return
302                 while (prev.real == 0):
303                         prev = prev.prev()
304                         if (prev == None):
305                                 return
306                 self.newevent(prev)
307
308         def fpress(self):
309                 next = self.event.next()
310                 if (next == None):
311                         return
312                 while (next.real == 0):
313                         next = next.next()
314                         if (next == None):
315                                 return
316                 self.newevent(next)
317
318         def linkpress(self, wevent):
319                 event = self.event.getlinked()
320                 if (event != None):
321                         self.newevent(event)
322
323 class Event:
324         name = "none"
325         color = "grey"
326         def __init__(self, source, cpu, timestamp, last=0):
327                 self.source = source
328                 self.cpu = cpu
329                 self.timestamp = int(timestamp)
330                 self.entries = []
331                 self.real = 1
332                 self.idx = None
333                 self.state = 0
334                 self.item = None
335                 self.dispcnt = 0
336                 self.linked = None
337                 if (last):
338                         source.lastevent(self)
339                 else:
340                         source.event(self)
341
342         def status(self):
343                 statstr = self.name + " " + self.source.name
344                 statstr += " on: cpu" + str(self.cpu)
345                 statstr += " at: " + str(self.timestamp)
346                 statstr += self.stattxt()
347                 status.set(statstr)
348
349         def stattxt(self):
350                 return ""
351
352         def textadd(self, tuple):
353                 pass
354                 self.entries.append(tuple)
355
356         def labels(self):
357                 return [("Source:", self.source.name, 0),
358                                 ("Event:", self.name, 0),
359                                 ("CPU:", self.cpu, 0),
360                                 ("Timestamp:", self.timestamp, 0)] + self.entries
361         def mouseenter(self, canvas, item):
362                 self.displayref(canvas)
363                 self.status()
364
365         def mouseexit(self, canvas, item):
366                 self.displayunref(canvas)
367                 status.clear()
368
369         def mousepress(self, canvas, item):
370                 EventView(self, canvas)
371
372         def next(self):
373                 return self.source.eventat(self.idx + 1)
374
375         def prev(self):
376                 return self.source.eventat(self.idx - 1)
377
378         def displayref(self, canvas):
379                 if (self.dispcnt == 0):
380                         canvas.itemconfigure(self.item, width=2)
381                 self.dispcnt += 1
382
383         def displayunref(self, canvas):
384                 self.dispcnt -= 1
385                 if (self.dispcnt == 0):
386                         canvas.itemconfigure(self.item, width=0)
387                         canvas.tag_raise("point", "state")
388
389         def getlinked(self):
390                 return self.linked.findevent(self.timestamp)
391
392 class PointEvent(Event):
393         def __init__(self, thread, cpu, timestamp, last=0):
394                 Event.__init__(self, thread, cpu, timestamp, last)
395
396         def draw(self, canvas, xpos, ypos):
397                 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
398                     fill=self.color, tags=("all", "point", "event")
399                     + (self.name,), width=0)
400                 canvas.events[l] = self
401                 self.item = l
402                 if (self.enabled == 0):
403                         canvas.itemconfigure(l, state="hidden")
404
405                 return (xpos)
406
407 class StateEvent(Event):
408         def __init__(self, thread, cpu, timestamp, last=0):
409                 Event.__init__(self, thread, cpu, timestamp, last)
410                 self.duration = 0
411                 self.skipnext = 0
412                 self.skipself = 0
413                 self.state = 1
414
415         def draw(self, canvas, xpos, ypos):
416                 next = self.nextstate()
417                 if (self.skipself == 1 or next == None):
418                         return (xpos)
419                 while (self.skipnext):
420                         skipped = next
421                         next.skipself = 1
422                         next.real = 0
423                         next = next.nextstate()
424                         if (next == None):
425                                 next = skipped
426                         self.skipnext -= 1
427                 self.duration = next.timestamp - self.timestamp
428                 if (self.duration < 0):
429                         self.duration = 0
430                         print "Unsynchronized timestamp"
431                         print self.cpu, self.timestamp
432                         print next.cpu, next.timestamp
433                 delta = self.duration / canvas.ratio
434                 l = canvas.create_rectangle(xpos, ypos,
435                     xpos + delta, ypos - 10, fill=self.color, width=0,
436                     tags=("all", "state", "event") + (self.name,))
437                 canvas.events[l] = self
438                 self.item = l
439                 if (self.enabled == 0):
440                         canvas.itemconfigure(l, state="hidden")
441
442                 return (xpos + delta)
443
444         def stattxt(self):
445                 return " duration: " + ticks2sec(self.duration)
446
447         def nextstate(self):
448                 next = self.next()
449                 while (next != None and next.state == 0):
450                         next = next.next()
451                 return (next)
452
453         def labels(self):
454                 return [("Source:", self.source.name, 0),
455                                 ("Event:", self.name, 0),
456                                 ("Timestamp:", self.timestamp, 0),
457                                 ("CPU:", self.cpu, 0),
458                                 ("Duration:", ticks2sec(self.duration), 0)] \
459                                  + self.entries
460
461 class Count(Event):
462         name = "Count"
463         color = "red"
464         enabled = 1
465         def __init__(self, source, cpu, timestamp, count):
466                 self.count = int(count)
467                 Event.__init__(self, source, cpu, timestamp)
468                 self.duration = 0
469                 self.textadd(("count:", self.count, 0))
470
471         def draw(self, canvas, xpos, ypos):
472                 next = self.next()
473                 self.duration = next.timestamp - self.timestamp
474                 delta = self.duration / canvas.ratio
475                 yhight = self.source.yscale() * self.count
476                 l = canvas.create_rectangle(xpos, ypos - yhight,
477                     xpos + delta, ypos, fill=self.color, width=0,
478                     tags=("all", "count", "event") + (self.name,))
479                 canvas.events[l] = self
480                 self.item = l
481                 if (self.enabled == 0):
482                         canvas.itemconfigure(l, state="hidden")
483                 return (xpos + delta)
484
485         def stattxt(self):
486                 return " count: " + str(self.count)
487
488 configtypes.append(Count)
489
490 class Running(StateEvent):
491         name = "running"
492         color = "green"
493         enabled = 1
494         def __init__(self, thread, cpu, timestamp, prio):
495                 StateEvent.__init__(self, thread, cpu, timestamp)
496                 self.prio = prio
497                 self.textadd(("prio:", self.prio, 0))
498
499 configtypes.append(Running)
500
501 class Idle(StateEvent):
502         name = "idle"
503         color = "grey"
504         enabled = 0
505         def __init__(self, thread, cpu, timestamp, prio):
506                 StateEvent.__init__(self, thread, cpu, timestamp)
507                 self.prio = prio
508                 self.textadd(("prio:", self.prio, 0))
509
510 configtypes.append(Idle)
511
512 class Yielding(StateEvent):
513         name = "yielding"
514         color = "yellow"
515         enabled = 1
516         def __init__(self, thread, cpu, timestamp, prio):
517                 StateEvent.__init__(self, thread, cpu, timestamp)
518                 self.skipnext = 1
519                 self.prio = prio
520                 self.textadd(("prio:", self.prio, 0))
521
522 configtypes.append(Yielding)
523
524 class Swapped(StateEvent):
525         name = "swapped"
526         color = "violet"
527         enabled = 1
528         def __init__(self, thread, cpu, timestamp, prio):
529                 StateEvent.__init__(self, thread, cpu, timestamp)
530                 self.prio = prio
531                 self.textadd(("prio:", self.prio, 0))
532
533 configtypes.append(Swapped)
534
535 class Suspended(StateEvent):
536         name = "suspended"
537         color = "purple"
538         enabled = 1
539         def __init__(self, thread, cpu, timestamp, prio):
540                 StateEvent.__init__(self, thread, cpu, timestamp)
541                 self.prio = prio
542                 self.textadd(("prio:", self.prio, 0))
543
544 configtypes.append(Suspended)
545
546 class Iwait(StateEvent):
547         name = "iwait"
548         color = "grey"
549         enabled = 0
550         def __init__(self, thread, cpu, timestamp, prio):
551                 StateEvent.__init__(self, thread, cpu, timestamp)
552                 self.prio = prio
553                 self.textadd(("prio:", self.prio, 0))
554
555 configtypes.append(Iwait)
556
557 class Preempted(StateEvent):
558         name = "preempted"
559         color = "red"
560         enabled = 1
561         def __init__(self, thread, cpu, timestamp, prio, bythread):
562                 StateEvent.__init__(self, thread, cpu, timestamp)
563                 self.skipnext = 1
564                 self.prio = prio
565                 self.linked = bythread
566                 self.textadd(("prio:", self.prio, 0))
567                 self.textadd(("by thread:", self.linked.name, 1))
568
569 configtypes.append(Preempted)
570
571 class Sleep(StateEvent):
572         name = "sleep"
573         color = "blue"
574         enabled = 1
575         def __init__(self, thread, cpu, timestamp, prio, wmesg):
576                 StateEvent.__init__(self, thread, cpu, timestamp)
577                 self.prio = prio
578                 self.wmesg = wmesg
579                 self.textadd(("prio:", self.prio, 0))
580                 self.textadd(("wmesg:", self.wmesg, 0))
581
582         def stattxt(self):
583                 statstr = StateEvent.stattxt(self)
584                 statstr += " sleeping on: " + self.wmesg
585                 return (statstr)
586
587 configtypes.append(Sleep)
588
589 class Blocked(StateEvent):
590         name = "blocked"
591         color = "dark red"
592         enabled = 1
593         def __init__(self, thread, cpu, timestamp, prio, lock):
594                 StateEvent.__init__(self, thread, cpu, timestamp)
595                 self.prio = prio
596                 self.lock = lock
597                 self.textadd(("prio:", self.prio, 0))
598                 self.textadd(("lock:", self.lock, 0))
599
600         def stattxt(self):
601                 statstr = StateEvent.stattxt(self)
602                 statstr += " blocked on: " + self.lock
603                 return (statstr)
604
605 configtypes.append(Blocked)
606
607 class KsegrpRunq(StateEvent):
608         name = "KsegrpRunq"
609         color = "orange"
610         enabled = 1
611         def __init__(self, thread, cpu, timestamp, prio, bythread):
612                 StateEvent.__init__(self, thread, cpu, timestamp)
613                 self.prio = prio
614                 self.linked = bythread
615                 self.textadd(("prio:", self.prio, 0))
616                 self.textadd(("by thread:", self.linked.name, 1))
617
618 configtypes.append(KsegrpRunq)
619
620 class Runq(StateEvent):
621         name = "Runq"
622         color = "yellow"
623         enabled = 1
624         def __init__(self, thread, cpu, timestamp, prio, bythread):
625                 StateEvent.__init__(self, thread, cpu, timestamp)
626                 self.prio = prio
627                 self.linked = bythread
628                 self.textadd(("prio:", self.prio, 0))
629                 self.textadd(("by thread:", self.linked.name, 1))
630
631 configtypes.append(Runq)
632
633 class Sched_exit(StateEvent):
634         name = "exit"
635         color = "grey"
636         enabled = 0
637         def __init__(self, thread, cpu, timestamp, prio):
638                 StateEvent.__init__(self, thread, cpu, timestamp)
639                 self.name = "sched_exit"
640                 self.prio = prio
641                 self.textadd(("prio:", self.prio, 0))
642
643 configtypes.append(Sched_exit)
644
645 class Padevent(StateEvent):
646         def __init__(self, thread, cpu, timestamp, last=0):
647                 StateEvent.__init__(self, thread, cpu, timestamp, last)
648                 self.name = "pad"
649                 self.real = 0
650
651         def draw(self, canvas, xpos, ypos):
652                 next = self.next()
653                 if (next == None):
654                         return (xpos)
655                 self.duration = next.timestamp - self.timestamp
656                 delta = self.duration / canvas.ratio
657                 return (xpos + delta)
658
659 class Tick(PointEvent):
660         name = "tick"
661         color = "black"
662         enabled = 0
663         def __init__(self, thread, cpu, timestamp, prio, stathz):
664                 PointEvent.__init__(self, thread, cpu, timestamp)
665                 self.prio = prio
666                 self.textadd(("prio:", self.prio, 0))
667
668 configtypes.append(Tick)
669
670 class Prio(PointEvent):
671         name = "prio"
672         color = "black"
673         enabled = 0
674         def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
675                 PointEvent.__init__(self, thread, cpu, timestamp)
676                 self.prio = prio
677                 self.newprio = newprio
678                 self.linked = bythread
679                 self.textadd(("new prio:", self.newprio, 0))
680                 self.textadd(("prio:", self.prio, 0))
681                 if (self.linked != self.source):
682                         self.textadd(("by thread:", self.linked.name, 1))
683                 else:
684                         self.textadd(("by thread:", self.linked.name, 0))
685
686 configtypes.append(Prio)
687
688 class Lend(PointEvent):
689         name = "lend"
690         color = "black"
691         enabled = 0
692         def __init__(self, thread, cpu, timestamp, prio, tothread):
693                 PointEvent.__init__(self, thread, cpu, timestamp)
694                 self.prio = prio
695                 self.linked = tothread
696                 self.textadd(("prio:", self.prio, 0))
697                 self.textadd(("to thread:", self.linked.name, 1))
698
699 configtypes.append(Lend)
700
701 class Wokeup(PointEvent):
702         name = "wokeup"
703         color = "black"
704         enabled = 0
705         def __init__(self, thread, cpu, timestamp, ranthread):
706                 PointEvent.__init__(self, thread, cpu, timestamp)
707                 self.linked = ranthread
708                 self.textadd(("ran thread:", self.linked.name, 1))
709
710 configtypes.append(Wokeup)
711
712 class EventSource:
713         def __init__(self, name):
714                 self.name = name
715                 self.events = []
716                 self.cpu = 0
717                 self.cpux = 0
718
719         def fixup(self):
720                 pass
721
722         def event(self, event):
723                 self.events.insert(0, event)
724
725         def remove(self, event):
726                 self.events.remove(event)
727
728         def lastevent(self, event):
729                 self.events.append(event)
730
731         def draw(self, canvas, ypos):
732                 xpos = 10
733                 self.cpux = 10
734                 self.cpu = self.events[1].cpu
735                 for i in range(0, len(self.events)):
736                         self.events[i].idx = i
737                 for event in self.events:
738                         if (event.cpu != self.cpu and event.cpu != -1):
739                                 self.drawcpu(canvas, xpos, ypos)
740                                 self.cpux = xpos
741                                 self.cpu = event.cpu
742                         xpos = event.draw(canvas, xpos, ypos)
743                 self.drawcpu(canvas, xpos, ypos)
744
745         def drawname(self, canvas, ypos):
746                 ypos = ypos - (self.ysize() / 2)
747                 canvas.create_text(10, ypos, anchor="w", text=self.name)
748
749         def drawcpu(self, canvas, xpos, ypos):
750                 cpu = int(self.cpu)
751                 if (cpu == 0):
752                         color = 'light grey'
753                 elif (cpu == 1):
754                         color = 'dark grey'
755                 elif (cpu == 2):
756                         color = 'light blue'
757                 elif (cpu == 3):
758                         color = 'light green'
759                 elif (cpu == 4):
760                         color = 'blanched almond'
761                 elif (cpu == 5):
762                         color = 'slate grey'
763                 elif (cpu == 6):
764                         color = 'light slate blue'
765                 elif (cpu == 7):
766                         color = 'thistle'
767                 else:
768                         color = "white"
769                 l = canvas.create_rectangle(self.cpux,
770                     ypos - self.ysize() - canvas.bdheight,
771                     xpos, ypos + canvas.bdheight, fill=color, width=0,
772                     tags=("all", "cpuinfo"))
773
774         def ysize(self):
775                 return (None)
776
777         def eventat(self, i):
778                 if (i >= len(self.events)):
779                         return (None)
780                 event = self.events[i]
781                 return (event)
782
783         def findevent(self, timestamp):
784                 for event in self.events:
785                         if (event.timestamp >= timestamp and event.real):
786                                 return (event)
787                 return (None)
788
789 class Thread(EventSource):
790         names = {}
791         def __init__(self, td, pcomm):
792                 EventSource.__init__(self, pcomm)
793                 self.str = td
794                 try:
795                         cnt = Thread.names[pcomm]
796                 except:
797                         Thread.names[pcomm] = 0
798                         return
799                 Thread.names[pcomm] = cnt + 1
800
801         def fixup(self):
802                 cnt = Thread.names[self.name]
803                 if (cnt == 0):
804                         return
805                 cnt -= 1
806                 Thread.names[self.name] = cnt
807                 self.name += " td" + str(cnt)
808
809         def ysize(self):
810                 return (10)
811
812 class Counter(EventSource):
813         max = 0
814         def __init__(self, name):
815                 EventSource.__init__(self, name)
816
817         def event(self, event):
818                 EventSource.event(self, event)
819                 try:
820                         count = event.count
821                 except:
822                         return
823                 count = int(count)
824                 if (count > Counter.max):
825                         Counter.max = count
826
827         def ysize(self):
828                 return (80)
829
830         def yscale(self):
831                 return (self.ysize() / Counter.max)
832
833
834 class KTRFile:
835         def __init__(self, file):
836                 self.timestamp_first = {}
837                 self.timestamp_last = {}
838                 self.timestamp_adjust = {}
839                 self.timestamp_f = None
840                 self.timestamp_l = None
841                 self.lineno = -1
842                 self.threads = []
843                 self.sources = []
844                 self.ticks = {}
845                 self.load = {}
846                 self.crit = {}
847                 self.stathz = 0
848
849                 self.parse(file)
850                 self.fixup()
851                 global ticksps
852                 print "first", self.timestamp_f, "last", self.timestamp_l
853                 print "time span", self.timespan()
854                 print "stathz", self.stathz
855                 ticksps = self.ticksps()
856                 print "Ticks per second", ticksps
857
858         def parse(self, file):
859                 try:
860                         ifp = open(file)
861                 except:
862                         print "Can't open", file
863                         sys.exit(1)
864
865                 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+"
866                 tdname = "(\S+)\(([^)]*)\)"
867                 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
868
869                 ktrstr = "mi_switch: " + tdname
870                 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
871                 switchout_re = re.compile(ktrhdr + ktrstr)
872
873                 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
874                 idled_re = re.compile(ktrhdr + ktrstr)
875
876                 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
877                 ktrstr += tdname
878                 preempted_re = re.compile(ktrhdr + ktrstr)
879
880                 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
881                 switchin_re = re.compile(ktrhdr + ktrstr)
882
883                 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
884                 sched_add_re = re.compile(ktrhdr + ktrstr)
885
886                 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
887                 setrunqueue_re = re.compile(ktrhdr + ktrstr)
888
889                 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
890                 sched_rem_re = re.compile(ktrhdr + ktrstr)
891
892                 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
893                 sched_exit_re = re.compile(ktrhdr + ktrstr)
894
895                 ktrstr = "statclock: " + tdname + " prio (\d+)"
896                 ktrstr += " stathz (\d+)"
897                 sched_clock_re = re.compile(ktrhdr + ktrstr)
898
899                 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
900                 ktrstr += " newprio (\d+) by " + tdname
901                 sched_prio_re = re.compile(ktrhdr + ktrstr)
902
903                 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
904                 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
905
906                 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
907                 critsec_re = re.compile(ktrhdr + ktrstr)
908
909                 parsers = [[cpuload_re, self.cpuload],
910                            [loadglobal_re, self.loadglobal],
911                            [switchin_re, self.switchin],
912                            [switchout_re, self.switchout],
913                            [sched_add_re, self.sched_add],
914                            [setrunqueue_re, self.sched_rem],
915                            [sched_prio_re, self.sched_prio],
916                            [preempted_re, self.preempted],
917                            [sched_rem_re, self.sched_rem],
918                            [sched_exit_re, self.sched_exit],
919                            [sched_clock_re, self.sched_clock],
920                            [critsec_re, self.critsec],
921                            [idled_re, self.idled]]
922
923                 lines = ifp.readlines()
924                 self.synchstamp(lines)
925                 for line in lines:
926                         self.lineno += 1
927                         if ((self.lineno % 1024) == 0):
928                                 status.startup("Parsing line " +
929                                     str(self.lineno))
930                         for p in parsers:
931                                 m = p[0].match(line)
932                                 if (m != None):
933                                         p[1](*m.groups())
934                                         break
935                         # if (m == None):
936                         #       print line,
937
938         def synchstamp(self, lines):
939                 status.startup("Rationalizing Timestamps")
940                 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*")
941                 for line in lines:
942                         m = tstamp_re.match(line)
943                         if (m != None):
944                                 self.addstamp(*m.groups())
945                 self.pickstamp()
946                 self.monostamp(lines)
947
948
949         def monostamp(self, lines):
950                 laststamp = None
951                 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*")
952                 for line in lines:
953                         m = tstamp_re.match(line)
954                         if (m == None):
955                                 continue
956                         (cpu, timestamp) = m.groups()
957                         timestamp = int(timestamp)
958                         cpu = int(cpu)
959                         timestamp -= self.timestamp_adjust[cpu]
960                         if (laststamp != None and timestamp > laststamp):
961                                 self.timestamp_adjust[cpu] += timestamp - laststamp
962                         laststamp = timestamp
963
964         def addstamp(self, cpu, timestamp):
965                 timestamp = int(timestamp)
966                 cpu = int(cpu)
967                 try:
968                         if (timestamp > self.timestamp_first[cpu]):
969                                 return
970                 except:
971                         self.timestamp_first[cpu] = timestamp
972                 self.timestamp_last[cpu] = timestamp
973
974         def pickstamp(self):
975                 base = self.timestamp_last[0]
976                 for i in range(0, len(self.timestamp_last)):
977                         if (self.timestamp_last[i] < base):
978                                 base = self.timestamp_last[i]
979
980                 print "Adjusting to base stamp", base
981                 for i in range(0, len(self.timestamp_last)):
982                         self.timestamp_adjust[i] = self.timestamp_last[i] - base;
983                         print "CPU ", i, "adjust by ", self.timestamp_adjust[i]
984
985                 self.timestamp_f = 0
986                 for i in range(0, len(self.timestamp_first)):
987                         first = self.timestamp_first[i] - self.timestamp_adjust[i]
988                         if (first > self.timestamp_f):
989                                 self.timestamp_f = first
990
991                 self.timestamp_l = 0
992                 for i in range(0, len(self.timestamp_last)):
993                         last = self.timestamp_last[i] - self.timestamp_adjust[i]
994                         if (last > self.timestamp_l):
995                                 self.timestamp_l = last
996
997
998         def checkstamp(self, cpu, timestamp):
999                 cpu = int(cpu)
1000                 timestamp = int(timestamp)
1001                 if (timestamp > self.timestamp_first[cpu]):
1002                         print "Bad timestamp on line ", self.lineno
1003                         return (0)
1004                 timestamp -= self.timestamp_adjust[cpu]
1005                 return (timestamp)
1006
1007         def timespan(self):
1008                 return (self.timestamp_f - self.timestamp_l);
1009
1010         def ticksps(self):
1011                 return (self.timespan() / self.ticks[0]) * int(self.stathz)
1012
1013         def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
1014                 TDI_SUSPENDED = 0x0001
1015                 TDI_SLEEPING = 0x0002
1016                 TDI_SWAPPED = 0x0004
1017                 TDI_LOCK = 0x0008
1018                 TDI_IWAIT = 0x0010 
1019
1020                 timestamp = self.checkstamp(cpu, timestamp)
1021                 if (timestamp == 0):
1022                         return
1023                 inhibit = int(inhibit)
1024                 thread = self.findtd(td, pcomm)
1025                 if (inhibit & TDI_SWAPPED):
1026                         Swapped(thread, cpu, timestamp, prio)
1027                 elif (inhibit & TDI_SLEEPING):
1028                         Sleep(thread, cpu, timestamp, prio, wmesg)
1029                 elif (inhibit & TDI_LOCK):
1030                         Blocked(thread, cpu, timestamp, prio, lock)
1031                 elif (inhibit & TDI_IWAIT):
1032                         Iwait(thread, cpu, timestamp, prio)
1033                 elif (inhibit & TDI_SUSPENDED):
1034                         Suspended(thread, cpu, timestamp, prio)
1035                 elif (inhibit == 0):
1036                         Yielding(thread, cpu, timestamp, prio)
1037                 else:
1038                         print "Unknown event", inhibit
1039                         sys.exit(1)
1040                 
1041         def idled(self, cpu, timestamp, td, pcomm, prio):
1042                 timestamp = self.checkstamp(cpu, timestamp)
1043                 if (timestamp == 0):
1044                         return
1045                 thread = self.findtd(td, pcomm)
1046                 Idle(thread, cpu, timestamp, prio)
1047
1048         def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1049                 timestamp = self.checkstamp(cpu, timestamp)
1050                 if (timestamp == 0):
1051                         return
1052                 thread = self.findtd(td, pcomm)
1053                 Preempted(thread, cpu, timestamp, prio,
1054                     self.findtd(bytd, bypcomm))
1055
1056         def switchin(self, cpu, timestamp, td, pcomm, prio):
1057                 timestamp = self.checkstamp(cpu, timestamp)
1058                 if (timestamp == 0):
1059                         return
1060                 thread = self.findtd(td, pcomm)
1061                 Running(thread, cpu, timestamp, prio)
1062
1063         def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1064                 timestamp = self.checkstamp(cpu, timestamp)
1065                 if (timestamp == 0):
1066                         return
1067                 thread = self.findtd(td, pcomm)
1068                 bythread = self.findtd(bytd, bypcomm)
1069                 Runq(thread, cpu, timestamp, prio, bythread)
1070                 Wokeup(bythread, cpu, timestamp, thread)
1071
1072         def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1073                 timestamp = self.checkstamp(cpu, timestamp)
1074                 if (timestamp == 0):
1075                         return
1076                 thread = self.findtd(td, pcomm)
1077                 KsegrpRunq(thread, cpu, timestamp, prio,
1078                     self.findtd(bytd, bypcomm))
1079
1080         def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1081                 timestamp = self.checkstamp(cpu, timestamp)
1082                 if (timestamp == 0):
1083                         return
1084                 thread = self.findtd(td, pcomm)
1085                 Sched_exit(thread, cpu, timestamp, prio)
1086
1087         def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1088                 timestamp = self.checkstamp(cpu, timestamp)
1089                 if (timestamp == 0):
1090                         return
1091                 self.stathz = stathz
1092                 cpu = int(cpu)
1093                 try:
1094                         ticks = self.ticks[cpu]
1095                 except:
1096                         self.ticks[cpu] = 0
1097                 self.ticks[cpu] += 1
1098                 thread = self.findtd(td, pcomm)
1099                 Tick(thread, cpu, timestamp, prio, stathz)
1100
1101         def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1102                 if (prio == newprio):
1103                         return
1104                 timestamp = self.checkstamp(cpu, timestamp)
1105                 if (timestamp == 0):
1106                         return
1107                 thread = self.findtd(td, pcomm)
1108                 bythread = self.findtd(bytd, bypcomm)
1109                 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1110                 Lend(bythread, cpu, timestamp, newprio, thread)
1111
1112         def cpuload(self, cpu, timestamp, count):
1113                 timestamp = self.checkstamp(cpu, timestamp)
1114                 if (timestamp == 0):
1115                         return
1116                 cpu = int(cpu)
1117                 try:
1118                         load = self.load[cpu]
1119                 except:
1120                         load = Counter("cpu" + str(cpu) + " load")
1121                         self.load[cpu] = load
1122                         self.sources.insert(0, load)
1123                 Count(load, cpu, timestamp, count)
1124
1125         def loadglobal(self, cpu, timestamp, count):
1126                 timestamp = self.checkstamp(cpu, timestamp)
1127                 if (timestamp == 0):
1128                         return
1129                 cpu = 0
1130                 try:
1131                         load = self.load[cpu]
1132                 except:
1133                         load = Counter("CPU load")
1134                         self.load[cpu] = load
1135                         self.sources.insert(0, load)
1136                 Count(load, cpu, timestamp, count)
1137
1138         def critsec(self, cpu, timestamp, td, pcomm, to):
1139                 timestamp = self.checkstamp(cpu, timestamp)
1140                 if (timestamp == 0):
1141                         return
1142                 cpu = int(cpu)
1143                 try:
1144                         crit = self.crit[cpu]
1145                 except:
1146                         crit = Counter("Critical Section")
1147                         self.crit[cpu] = crit
1148                         self.sources.insert(0, crit)
1149                 Count(crit, cpu, timestamp, to)
1150
1151         def findtd(self, td, pcomm):
1152                 for thread in self.threads:
1153                         if (thread.str == td and thread.name == pcomm):
1154                                 return thread
1155                 thread = Thread(td, pcomm)
1156                 self.threads.append(thread)
1157                 self.sources.append(thread)
1158                 return (thread)
1159
1160         def fixup(self):
1161                 for source in self.sources:
1162                         Padevent(source, -1, self.timestamp_l)
1163                         Padevent(source, -1, self.timestamp_f, last=1)
1164                         source.fixup()
1165
1166 class SchedDisplay(Canvas):
1167         def __init__(self, master):
1168                 self.ratio = 1
1169                 self.ktrfile = None
1170                 self.sources = None
1171                 self.bdheight = 10 
1172                 self.events = {}
1173
1174                 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1175                      scrollregion=(0, 0, 800, 500))
1176
1177         def setfile(self, ktrfile):
1178                 self.ktrfile = ktrfile
1179                 self.sources = ktrfile.sources
1180
1181         def draw(self):
1182                 ypos = 0
1183                 xsize = self.xsize()
1184                 for source in self.sources:
1185                         status.startup("Drawing " + source.name)
1186                         self.create_line(0, ypos, xsize, ypos,
1187                             width=1, fill="black", tags=("all",))
1188                         ypos += self.bdheight
1189                         ypos += source.ysize()
1190                         source.draw(self, ypos)
1191                         ypos += self.bdheight
1192                         try:
1193                                 self.tag_raise("point", "state")
1194                                 self.tag_lower("cpuinfo", "all")
1195                         except:
1196                                 pass
1197                 self.create_line(0, ypos, xsize, ypos,
1198                     width=1, fill="black", tags=("all",))
1199                 self.tag_bind("event", "<Enter>", self.mouseenter)
1200                 self.tag_bind("event", "<Leave>", self.mouseexit)
1201                 self.tag_bind("event", "<Button-1>", self.mousepress)
1202
1203         def mouseenter(self, event):
1204                 item, = self.find_withtag(CURRENT)
1205                 event = self.events[item]
1206                 event.mouseenter(self, item)
1207
1208         def mouseexit(self, event):
1209                 item, = self.find_withtag(CURRENT)
1210                 event = self.events[item]
1211                 event.mouseexit(self, item)
1212
1213         def mousepress(self, event):
1214                 item, = self.find_withtag(CURRENT)
1215                 event = self.events[item]
1216                 event.mousepress(self, item)
1217
1218         def drawnames(self, canvas):
1219                 status.startup("Drawing names")
1220                 ypos = 0
1221                 canvas.configure(scrollregion=(0, 0,
1222                     canvas["width"], self.ysize()))
1223                 for source in self.sources:
1224                         canvas.create_line(0, ypos, canvas["width"], ypos,
1225                             width=1, fill="black", tags=("all",))
1226                         ypos += self.bdheight
1227                         ypos += source.ysize()
1228                         source.drawname(canvas, ypos)
1229                         ypos += self.bdheight
1230                 canvas.create_line(0, ypos, canvas["width"], ypos,
1231                     width=1, fill="black", tags=("all",))
1232
1233         def xsize(self):
1234                 return ((self.ktrfile.timespan() / self.ratio) + 20)
1235
1236         def ysize(self):
1237                 ysize = 0
1238                 for source in self.sources:
1239                         ysize += source.ysize() + (self.bdheight * 2)
1240                 return (ysize)
1241
1242         def scaleset(self, ratio):
1243                 if (self.ktrfile == None):
1244                         return
1245                 oldratio = self.ratio
1246                 xstart, ystart = self.xview()
1247                 length = (float(self["width"]) / self.xsize())
1248                 middle = xstart + (length / 2)
1249
1250                 self.ratio = ratio
1251                 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1252                 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1253
1254                 length = (float(self["width"]) / self.xsize())
1255                 xstart = middle - (length / 2)
1256                 self.xview_moveto(xstart)
1257
1258         def scaleget(self):
1259                 return self.ratio
1260
1261         def setcolor(self, tag, color):
1262                 self.itemconfigure(tag, state="normal", fill=color)
1263
1264         def hide(self, tag):
1265                 self.itemconfigure(tag, state="hidden")
1266
1267 class GraphMenu(Frame):
1268         def __init__(self, master):
1269                 Frame.__init__(self, master, bd=2, relief=RAISED)
1270                 self.view = Menubutton(self, text="Configure")
1271                 self.viewmenu = Menu(self.view, tearoff=0)
1272                 self.viewmenu.add_command(label="Events",
1273                     command=self.econf)
1274                 self.view["menu"] = self.viewmenu
1275                 self.view.pack(side=LEFT)
1276
1277         def econf(self):
1278                 EventConfigure()
1279
1280
1281 class SchedGraph(Frame):
1282         def __init__(self, master):
1283                 Frame.__init__(self, master)
1284                 self.menu = None
1285                 self.names = None
1286                 self.display = None
1287                 self.scale = None
1288                 self.status = None
1289                 self.pack(expand=1, fill="both")
1290                 self.buildwidgets()
1291                 self.layout()
1292                 self.draw(sys.argv[1])
1293
1294         def buildwidgets(self):
1295                 global status
1296                 self.menu = GraphMenu(self)
1297                 self.display = SchedDisplay(self)
1298                 self.names = Canvas(self,
1299                     width=100, height=self.display["height"],
1300                     bg='grey', scrollregion=(0, 0, 50, 100))
1301                 self.scale = Scaler(self, self.display)
1302                 status = self.status = Status(self)
1303                 self.scrollY = Scrollbar(self, orient="vertical",
1304                     command=self.display_yview)
1305                 self.display.scrollX = Scrollbar(self, orient="horizontal",
1306                     command=self.display.xview)
1307                 self.display["xscrollcommand"] = self.display.scrollX.set
1308                 self.display["yscrollcommand"] = self.scrollY.set
1309                 self.names["yscrollcommand"] = self.scrollY.set
1310
1311         def layout(self):
1312                 self.columnconfigure(1, weight=1)
1313                 self.rowconfigure(1, weight=1)
1314                 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1315                 self.names.grid(row=1, column=0, sticky=N+S)
1316                 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1317                 self.scrollY.grid(row=1, column=2, sticky=N+S)
1318                 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1319                     sticky=E+W)
1320                 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1321                 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1322
1323         def draw(self, file):
1324                 self.master.update()
1325                 ktrfile = KTRFile(file)
1326                 self.display.setfile(ktrfile)
1327                 self.display.drawnames(self.names)
1328                 self.display.draw()
1329                 self.scale.set(250000)
1330                 self.display.xview_moveto(0)
1331
1332         def display_yview(self, *args):
1333                 self.names.yview(*args)
1334                 self.display.yview(*args)
1335
1336         def setcolor(self, tag, color):
1337                 self.display.setcolor(tag, color)
1338
1339         def hide(self, tag):
1340                 self.display.hide(tag)
1341
1342 if (len(sys.argv) != 2):
1343         print "usage:", sys.argv[0], "<ktr file>"
1344         sys.exit(1)
1345
1346 root = Tk()
1347 root.title("Scheduler Graph")
1348 graph = SchedGraph(root)
1349 root.mainloop()