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