]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - tools/sched/schedgraph.py
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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 # Events for running callout routines
676
677 class CalloutIdle(StateEvent):
678         name = "callwheel idle"
679         color = "grey"
680         enabled = 0
681         def __init__(self, wheel, cpu, timestamp):
682                 StateEvent.__init__(self, wheel, cpu, timestamp)
683
684 configtypes.append(CalloutIdle)
685
686 class CalloutRunning(StateEvent):
687         name = "callout running"
688         color = "green"
689         enabled = 1
690         def __init__(self, wheel, cpu, timestamp, func, arg):
691                 StateEvent.__init__(self, wheel, cpu, timestamp)
692                 self.textadd(("function:", func, 0))
693                 self.textadd(("argument:", arg, 0))
694                 self.arg = arg
695                 self.func = func
696
697         def stattxt(self):
698                 statstr = StateEvent.stattxt(self)
699                 statstr += " executing %s(%s)" % (self.func, self.arg)
700                 return (statstr)
701
702 configtypes.append(CalloutRunning)
703
704 # Events on locks
705 #
706 # XXX: No support for upgrade/downgrade currently or differentiating
707 # between read/write in general.
708 #
709 # XXX: Point events for recursion perhaps?
710
711 class LockAcquire(StateEvent):
712         name = "lock acquire"
713         color = "blue"
714         enabled = 1
715         def __init__(self, lock, cpu, timestamp, file, line):
716                 StateEvent.__init__(self, lock, cpu, timestamp)
717                 self.textadd(("file:", file, 0))
718                 self.textadd(("line:", line, 0))
719
720 configtypes.append(LockAcquire)
721
722 class LockContest(StateEvent):
723         name = "lock contest"
724         color = "purple"
725         enabled = 1
726         def __init__(self, lock, cpu, timestamp, file, line):
727                 StateEvent.__init__(self, lock, cpu, timestamp)
728                 self.textadd(("file:", file, 0))
729                 self.textadd(("line:", line, 0))
730
731 configtypes.append(LockContest)
732
733 class LockFailedTry(PointEvent):
734         name = "failed lock try"
735         color = "red"
736         enabled = 1
737         def __init__(self, lock, cpu, timestamp, file, line):
738                 PointEvent.__init__(self, lock, cpu, timestamp)
739                 self.textadd(("file:", file, 0))
740                 self.textadd(("line:", line, 0))
741
742 configtypes.append(LockFailedTry)
743
744 class LockRelease(StateEvent):
745         name = "lock release"
746         color = "grey"
747         enabled = 0
748         def __init__(self, lock, cpu, timestamp, file, line):
749                 StateEvent.__init__(self, lock, cpu, timestamp)
750                 self.textadd(("file:", file, 0))
751                 self.textadd(("line:", line, 0))
752
753 configtypes.append(LockRelease)
754
755 class Padevent(StateEvent):
756         def __init__(self, thread, cpu, timestamp, last=0):
757                 StateEvent.__init__(self, thread, cpu, timestamp, last)
758                 self.name = "pad"
759                 self.real = 0
760
761         def draw(self, canvas, xpos, ypos):
762                 next = self.next()
763                 if (next == None):
764                         return (xpos)
765                 self.duration = next.timestamp - self.timestamp
766                 delta = self.duration / canvas.ratio
767                 return (xpos + delta)
768
769 class Tick(PointEvent):
770         name = "tick"
771         color = "black"
772         enabled = 0
773         def __init__(self, thread, cpu, timestamp, prio, stathz):
774                 PointEvent.__init__(self, thread, cpu, timestamp)
775                 self.prio = prio
776                 self.textadd(("prio:", self.prio, 0))
777
778 configtypes.append(Tick)
779
780 class Prio(PointEvent):
781         name = "prio"
782         color = "black"
783         enabled = 0
784         def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
785                 PointEvent.__init__(self, thread, cpu, timestamp)
786                 self.prio = prio
787                 self.newprio = newprio
788                 self.linked = bythread
789                 self.textadd(("new prio:", self.newprio, 0))
790                 self.textadd(("prio:", self.prio, 0))
791                 if (self.linked != self.source):
792                         self.textadd(("by thread:", self.linked.name, 1))
793                 else:
794                         self.textadd(("by thread:", self.linked.name, 0))
795
796 configtypes.append(Prio)
797
798 class Lend(PointEvent):
799         name = "lend"
800         color = "black"
801         enabled = 0
802         def __init__(self, thread, cpu, timestamp, prio, tothread):
803                 PointEvent.__init__(self, thread, cpu, timestamp)
804                 self.prio = prio
805                 self.linked = tothread
806                 self.textadd(("prio:", self.prio, 0))
807                 self.textadd(("to thread:", self.linked.name, 1))
808
809 configtypes.append(Lend)
810
811 class Wokeup(PointEvent):
812         name = "wokeup"
813         color = "black"
814         enabled = 0
815         def __init__(self, thread, cpu, timestamp, ranthread):
816                 PointEvent.__init__(self, thread, cpu, timestamp)
817                 self.linked = ranthread
818                 self.textadd(("ran thread:", self.linked.name, 1))
819
820 configtypes.append(Wokeup)
821
822 (DEFAULT, LOAD, COUNT, CALLWHEEL, LOCK, THREAD) = range(6)
823
824 class EventSource:
825         def __init__(self, name, group=DEFAULT, order=0):
826                 self.name = name
827                 self.events = []
828                 self.cpu = 0
829                 self.cpux = 0
830                 self.group = group
831                 self.order = order
832
833         def __cmp__(self, other):
834                 if (self.group == other.group):
835                         return cmp(self.order, other.order)
836                 return cmp(self.group, other.group)
837
838         # It is much faster to append items to a list then to insert them
839         # at the beginning.  As a result, we add events in reverse order
840         # and then swap the list during fixup.
841         def fixup(self):
842                 self.events.reverse()
843
844         def event(self, event):
845                 self.events.append(event)
846
847         def remove(self, event):
848                 self.events.remove(event)
849
850         def lastevent(self, event):
851                 self.events.insert(0, event)
852
853         def draw(self, canvas, ypos):
854                 xpos = 10
855                 self.cpux = 10
856                 self.cpu = self.events[1].cpu
857                 for i in range(0, len(self.events)):
858                         self.events[i].idx = i
859                 for event in self.events:
860                         if (event.cpu != self.cpu and event.cpu != -1):
861                                 self.drawcpu(canvas, xpos, ypos)
862                                 self.cpux = xpos
863                                 self.cpu = event.cpu
864                         xpos = event.draw(canvas, xpos, ypos)
865                 self.drawcpu(canvas, xpos, ypos)
866
867         def drawname(self, canvas, ypos):
868                 ypos = ypos - (self.ysize() / 2)
869                 canvas.create_text(10, ypos, anchor="w", text=self.name)
870
871         def drawcpu(self, canvas, xpos, ypos):
872                 cpu = int(self.cpu)
873                 if (cpu == 0):
874                         color = 'light grey'
875                 elif (cpu == 1):
876                         color = 'dark grey'
877                 elif (cpu == 2):
878                         color = 'light blue'
879                 elif (cpu == 3):
880                         color = 'light green'
881                 elif (cpu == 4):
882                         color = 'blanched almond'
883                 elif (cpu == 5):
884                         color = 'slate grey'
885                 elif (cpu == 6):
886                         color = 'light slate blue'
887                 elif (cpu == 7):
888                         color = 'thistle'
889                 else:
890                         color = "white"
891                 l = canvas.create_rectangle(self.cpux,
892                     ypos - self.ysize() - canvas.bdheight,
893                     xpos, ypos + canvas.bdheight, fill=color, width=0,
894                     tags=("all", "cpuinfo"))
895
896         def ysize(self):
897                 return (None)
898
899         def eventat(self, i):
900                 if (i >= len(self.events)):
901                         return (None)
902                 event = self.events[i]
903                 return (event)
904
905         def findevent(self, timestamp):
906                 for event in self.events:
907                         if (event.timestamp >= timestamp and event.real):
908                                 return (event)
909                 return (None)
910
911 class Thread(EventSource):
912         names = {}
913         def __init__(self, td, pcomm):
914                 EventSource.__init__(self, pcomm, THREAD)
915                 self.str = td
916                 try:
917                         cnt = Thread.names[pcomm]
918                 except:
919                         Thread.names[pcomm] = 0
920                         return
921                 Thread.names[pcomm] = cnt + 1
922
923         def fixup(self):
924                 EventSource.fixup(self)
925                 cnt = Thread.names[self.name]
926                 if (cnt == 0):
927                         return
928                 cnt -= 1
929                 Thread.names[self.name] = cnt
930                 self.name += " td" + str(cnt)
931
932         def ysize(self):
933                 return (10)
934
935 class Callwheel(EventSource):
936         count = 0
937         def __init__(self, cpu):
938                 EventSource.__init__(self, "Callwheel", CALLWHEEL, cpu)
939                 self.wheel = cpu
940                 Callwheel.count += 1
941
942         def fixup(self):
943                 EventSource.fixup(self)
944                 if (Callwheel.count == 1):
945                         return
946                 self.name += " (CPU %d)" % (self.wheel)
947
948         def ysize(self):
949                 return (10)
950
951 class Lock(EventSource):
952         def __init__(self, lock):
953                 EventSource.__init__(self, lock, LOCK)
954
955         def ysize(self):
956                 return (10)
957
958 class Counter(EventSource):
959         max = 0
960         def __init__(self, name):
961                 EventSource.__init__(self, name, COUNT)
962
963         def event(self, event):
964                 EventSource.event(self, event)
965                 try:
966                         count = event.count
967                 except:
968                         return
969                 count = int(count)
970                 if (count > Counter.max):
971                         Counter.max = count
972
973         def ymax(self):
974                 return (Counter.max)
975
976         def ysize(self):
977                 return (80)
978
979         def yscale(self):
980                 return (self.ysize() / Counter.max)
981
982 class CPULoad(Counter):
983         def __init__(self, cpu):
984                 Counter.__init__(self, "cpu" + str(cpu) + " load")
985                 self.group = LOAD
986                 self.order = cpu
987
988 class KTRFile:
989         def __init__(self, file):
990                 self.timestamp_f = None
991                 self.timestamp_l = None
992                 self.threads = []
993                 self.sources = []
994                 self.locks = {}
995                 self.callwheels = {}
996                 self.ticks = {}
997                 self.load = {}
998                 self.crit = {}
999                 self.stathz = 0
1000
1001                 self.parse(file)
1002                 self.fixup()
1003                 global ticksps
1004                 print "first", self.timestamp_f, "last", self.timestamp_l
1005                 print "time span", self.timespan()
1006                 print "stathz", self.stathz
1007                 ticksps = self.ticksps()
1008                 print "Ticks per second", ticksps
1009
1010         def parse(self, file):
1011                 try:
1012                         ifp = open(file)
1013                 except:
1014                         print "Can't open", file
1015                         sys.exit(1)
1016
1017                 ktrhdr = "\s*\d+\s+(\d+)\s+(\d+)\s+"
1018                 tdname = "(\S+)\(([^)]*)\)"
1019                 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
1020
1021 # XXX doesn't handle:
1022 #   371   0      61628682318 mi_switch: 0xc075c070(swapper) prio 180 inhibit 2 wmesg ATA request done lock (null)
1023                 ktrstr = "mi_switch: " + tdname
1024                 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
1025                 switchout_re = re.compile(ktrhdr + ktrstr)
1026
1027                 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
1028                 idled_re = re.compile(ktrhdr + ktrstr)
1029
1030                 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
1031                 ktrstr += tdname
1032                 preempted_re = re.compile(ktrhdr + ktrstr)
1033
1034                 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
1035                 switchin_re = re.compile(ktrhdr + ktrstr)
1036
1037                 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
1038                 sched_add_re = re.compile(ktrhdr + ktrstr)
1039
1040                 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
1041                 setrunqueue_re = re.compile(ktrhdr + ktrstr)
1042
1043                 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
1044                 sched_rem_re = re.compile(ktrhdr + ktrstr)
1045
1046                 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
1047                 sched_exit_thread_re = re.compile(ktrhdr + ktrstr)
1048
1049                 ktrstr = "sched_exit: " + tdname + " prio (\d+)"
1050                 sched_exit_re = re.compile(ktrhdr + ktrstr)
1051
1052                 ktrstr = "statclock: " + tdname + " prio (\d+)"
1053                 ktrstr += " stathz (\d+)"
1054                 sched_clock_re = re.compile(ktrhdr + ktrstr)
1055
1056                 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
1057                 ktrstr += " newprio (\d+) by " + tdname
1058                 sched_prio_re = re.compile(ktrhdr + ktrstr)
1059
1060                 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
1061                 cpuload2_re = re.compile(ktrhdr + "cpu (\d+) load: (\d+)")
1062                 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
1063
1064                 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
1065                 critsec_re = re.compile(ktrhdr + ktrstr)
1066
1067                 ktrstr = "callout 0x[a-f\d]+ "
1068                 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1069                 callout_start_re = re.compile(ktrhdr + ktrstr)
1070
1071                 ktrstr = "callout mpsafe 0x[a-f\d]+ "
1072                 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1073                 callout_mpsafe_re = re.compile(ktrhdr + ktrstr)
1074
1075                 ktrstr = "callout mtx 0x[a-f\d]+ "
1076                 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1077                 callout_mtx_re = re.compile(ktrhdr + ktrstr)
1078                 
1079                 ktrstr = "callout 0x[a-f\d]+ finished"
1080                 callout_stop_re = re.compile(ktrhdr + ktrstr)
1081
1082                 ktrstr = "TRY_([RSWX]?LOCK) \(.*\) (.*) r = ([0-9]+)"
1083                 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1084                 lock_try_re = re.compile(ktrhdr + ktrstr)
1085
1086                 ktrstr = "([RSWX]?UNLOCK) \(.*\) (.*) r = ([0-9]+)"
1087                 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1088                 lock_release_re = re.compile(ktrhdr + ktrstr)
1089
1090                 ktrstr = "([RSWX]?LOCK) \(.*\) (.*) r = ([0-9]+)"
1091                 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1092                 lock_acquire_re = re.compile(ktrhdr + ktrstr)
1093
1094                 ktrstr = "_mtx_lock_sleep: (.*) contested \(lock=0x?[0-9a-f]*\)"
1095                 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1096                 mtx_contested_re = re.compile(ktrhdr + ktrstr)
1097
1098                 # XXX: Spin lock traces don't have lock name or file/line
1099
1100                 ktrstr = "_rw_wlock_hard: (.*) contested \(lock=0x?[0-9a-f]*\)"
1101                 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1102                 rw_contested_re = re.compile(ktrhdr + ktrstr)
1103
1104                 # XXX: Read lock traces for rwlocks contesting don't have
1105                 # lock name or file/line                
1106                 
1107                 parsers = [[cpuload_re, self.cpuload],
1108                            [cpuload2_re, self.cpuload2],
1109                            [loadglobal_re, self.loadglobal],
1110                            [switchin_re, self.switchin],
1111                            [switchout_re, self.switchout],
1112                            [sched_add_re, self.sched_add],
1113                            [setrunqueue_re, self.sched_rem],
1114                            [sched_prio_re, self.sched_prio],
1115                            [preempted_re, self.preempted],
1116                            [sched_rem_re, self.sched_rem],
1117                            [sched_exit_thread_re, self.sched_exit_thread],
1118                            [sched_exit_re, self.sched_exit],
1119                            [sched_clock_re, self.sched_clock],
1120                            [critsec_re, self.critsec],
1121                            [callout_start_re, self.callout_start],
1122                            [callout_mpsafe_re, self.callout_start],
1123                            [callout_mtx_re, self.callout_start],
1124                            [callout_stop_re, self.callout_stop],
1125                            [lock_try_re, self.lock_try],
1126                            [lock_release_re, self.lock_release],
1127                            [lock_acquire_re, self.lock_acquire],
1128                            [mtx_contested_re, self.lock_contest],
1129                            [rw_contested_re, self.lock_contest],
1130                            [idled_re, self.idled]]
1131
1132                 global lineno
1133                 lineno = 0
1134                 for line in ifp.readlines():
1135                         lineno += 1
1136                         if ((lineno % 1024) == 0):
1137                                 status.startup("Parsing line " + str(lineno))
1138                         for p in parsers:
1139                                 m = p[0].match(line)
1140                                 if (m != None):
1141                                         p[1](*m.groups())
1142                                         break
1143                         if (m == None):
1144                                 print line,
1145
1146         def checkstamp(self, cpu, timestamp):
1147                 timestamp = int(timestamp)
1148                 if (self.timestamp_f == None):
1149                         self.timestamp_f = timestamp;
1150                 if (self.timestamp_l != None and timestamp > self.timestamp_l):
1151                         return (0)
1152                 self.timestamp_l = timestamp;
1153                 return (timestamp)
1154
1155         def timespan(self):
1156                 return (self.timestamp_f - self.timestamp_l);
1157
1158         def ticksps(self):
1159                 return (self.timespan() / self.ticks[0]) * int(self.stathz)
1160
1161         def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
1162                 TDI_SUSPENDED = 0x0001
1163                 TDI_SLEEPING = 0x0002
1164                 TDI_SWAPPED = 0x0004
1165                 TDI_LOCK = 0x0008
1166                 TDI_IWAIT = 0x0010 
1167
1168                 timestamp = self.checkstamp(cpu, timestamp)
1169                 if (timestamp == 0):
1170                         return
1171                 inhibit = int(inhibit)
1172                 thread = self.findtd(td, pcomm)
1173                 if (inhibit & TDI_SWAPPED):
1174                         Swapped(thread, cpu, timestamp, prio)
1175                 elif (inhibit & TDI_SLEEPING):
1176                         Sleep(thread, cpu, timestamp, prio, wmesg)
1177                 elif (inhibit & TDI_LOCK):
1178                         Blocked(thread, cpu, timestamp, prio, lock)
1179                 elif (inhibit & TDI_IWAIT):
1180                         Iwait(thread, cpu, timestamp, prio)
1181                 elif (inhibit & TDI_SUSPENDED):
1182                         Suspended(thread, cpu, timestamp, prio)
1183                 elif (inhibit == 0):
1184                         Yielding(thread, cpu, timestamp, prio)
1185                 else:
1186                         print "Unknown event", inhibit
1187                         sys.exit(1)
1188                 
1189         def idled(self, cpu, timestamp, td, pcomm, prio):
1190                 timestamp = self.checkstamp(cpu, timestamp)
1191                 if (timestamp == 0):
1192                         return
1193                 thread = self.findtd(td, pcomm)
1194                 Idle(thread, cpu, timestamp, prio)
1195
1196         def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1197                 timestamp = self.checkstamp(cpu, timestamp)
1198                 if (timestamp == 0):
1199                         return
1200                 thread = self.findtd(td, pcomm)
1201                 Preempted(thread, cpu, timestamp, prio,
1202                     self.findtd(bytd, bypcomm))
1203
1204         def switchin(self, cpu, timestamp, td, pcomm, prio):
1205                 timestamp = self.checkstamp(cpu, timestamp)
1206                 if (timestamp == 0):
1207                         return
1208                 thread = self.findtd(td, pcomm)
1209                 Running(thread, cpu, timestamp, prio)
1210
1211         def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1212                 timestamp = self.checkstamp(cpu, timestamp)
1213                 if (timestamp == 0):
1214                         return
1215                 thread = self.findtd(td, pcomm)
1216                 bythread = self.findtd(bytd, bypcomm)
1217                 Runq(thread, cpu, timestamp, prio, bythread)
1218                 Wokeup(bythread, cpu, timestamp, thread)
1219
1220         def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1221                 timestamp = self.checkstamp(cpu, timestamp)
1222                 if (timestamp == 0):
1223                         return
1224                 thread = self.findtd(td, pcomm)
1225                 KsegrpRunq(thread, cpu, timestamp, prio,
1226                     self.findtd(bytd, bypcomm))
1227
1228         def sched_exit_thread(self, cpu, timestamp, td, pcomm, prio):
1229                 timestamp = self.checkstamp(cpu, timestamp)
1230                 if (timestamp == 0):
1231                         return
1232                 thread = self.findtd(td, pcomm)
1233                 Sched_exit_thread(thread, cpu, timestamp, prio)
1234
1235         def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1236                 timestamp = self.checkstamp(cpu, timestamp)
1237                 if (timestamp == 0):
1238                         return
1239                 thread = self.findtd(td, pcomm)
1240                 Sched_exit(thread, cpu, timestamp, prio)
1241
1242         def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1243                 timestamp = self.checkstamp(cpu, timestamp)
1244                 if (timestamp == 0):
1245                         return
1246                 self.stathz = stathz
1247                 cpu = int(cpu)
1248                 try:
1249                         ticks = self.ticks[cpu]
1250                 except:
1251                         self.ticks[cpu] = 0
1252                 self.ticks[cpu] += 1
1253                 thread = self.findtd(td, pcomm)
1254                 Tick(thread, cpu, timestamp, prio, stathz)
1255
1256         def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1257                 if (prio == newprio):
1258                         return
1259                 timestamp = self.checkstamp(cpu, timestamp)
1260                 if (timestamp == 0):
1261                         return
1262                 thread = self.findtd(td, pcomm)
1263                 bythread = self.findtd(bytd, bypcomm)
1264                 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1265                 Lend(bythread, cpu, timestamp, newprio, thread)
1266
1267         def cpuload(self, cpu, timestamp, count):
1268                 timestamp = self.checkstamp(cpu, timestamp)
1269                 if (timestamp == 0):
1270                         return
1271                 cpu = int(cpu)
1272                 try:
1273                         load = self.load[cpu]
1274                 except:
1275                         load = CPULoad(cpu)
1276                         self.load[cpu] = load
1277                         self.sources.append(load)
1278                 Count(load, cpu, timestamp, count)
1279
1280         def cpuload2(self, cpu, timestamp, ncpu, count):
1281                 timestamp = self.checkstamp(cpu, timestamp)
1282                 if (timestamp == 0):
1283                         return
1284                 cpu = int(ncpu)
1285                 try:
1286                         load = self.load[cpu]
1287                 except:
1288                         load = CPULoad(cpu)
1289                         self.load[cpu] = load
1290                         self.sources.append(load)
1291                 Count(load, cpu, timestamp, count)
1292
1293         def loadglobal(self, cpu, timestamp, count):
1294                 timestamp = self.checkstamp(cpu, timestamp)
1295                 if (timestamp == 0):
1296                         return
1297                 cpu = 0
1298                 try:
1299                         load = self.load[cpu]
1300                 except:
1301                         load = Counter("CPU load")
1302                         self.load[cpu] = load
1303                         self.sources.append(load)
1304                 Count(load, cpu, timestamp, count)
1305
1306         def critsec(self, cpu, timestamp, td, pcomm, to):
1307                 timestamp = self.checkstamp(cpu, timestamp)
1308                 if (timestamp == 0):
1309                         return
1310                 cpu = int(cpu)
1311                 try:
1312                         crit = self.crit[cpu]
1313                 except:
1314                         crit = Counter("Critical Section")
1315                         self.crit[cpu] = crit
1316                         self.sources.append(crit)
1317                 Count(crit, cpu, timestamp, to)
1318
1319         def callout_start(self, cpu, timestamp, func, arg):
1320                 timestamp = self.checkstamp(cpu, timestamp)
1321                 if (timestamp == 0):
1322                         return
1323                 wheel = self.findwheel(cpu)
1324                 CalloutRunning(wheel, cpu, timestamp, func, arg)
1325
1326         def callout_stop(self, cpu, timestamp):
1327                 timestamp = self.checkstamp(cpu, timestamp)
1328                 if (timestamp == 0):
1329                         return
1330                 wheel = self.findwheel(cpu)
1331                 CalloutIdle(wheel, cpu, timestamp)
1332
1333         def lock_try(self, cpu, timestamp, op, name, result, file, line):
1334                 timestamp = self.checkstamp(cpu, timestamp)
1335                 if (timestamp == 0):
1336                         return
1337                 lock = self.findlock(name)
1338                 if (int(result) == 0):
1339                         LockFailedTry(lock, cpu, timestamp, file, line)
1340                 else:
1341                         LockAcquire(lock, cpu, timestamp, file, line)
1342
1343         def lock_acquire(self, cpu, timestamp, op, name, recurse, file, line):
1344                 if (int(recurse) != 0):
1345                         return
1346                 timestamp = self.checkstamp(cpu, timestamp)
1347                 if (timestamp == 0):
1348                         return
1349                 lock = self.findlock(name)
1350                 LockAcquire(lock, cpu, timestamp, file, line)
1351                 
1352         def lock_release(self, cpu, timestamp, op, name, recurse, file, line):
1353                 if (int(recurse) != 0):
1354                         return
1355                 timestamp = self.checkstamp(cpu, timestamp)
1356                 if (timestamp == 0):
1357                         return
1358                 lock = self.findlock(name)
1359                 LockRelease(lock, cpu, timestamp, file, line)
1360
1361         def lock_contest(self, cpu, timestamp, name, file, line):
1362                 timestamp = self.checkstamp(cpu, timestamp)
1363                 if (timestamp == 0):
1364                         return
1365                 lock = self.findlock(name)
1366                 LockContest(lock, cpu, timestamp, file, line)
1367
1368         def findlock(self, name):
1369                 try:
1370                         lock = self.locks[name]
1371                 except:
1372                         lock = Lock(name)
1373                         self.locks[name] = lock
1374                         self.sources.append(lock)
1375                 return (lock)
1376
1377         def findwheel(self, cpu):
1378                 cpu = int(cpu)
1379                 try:
1380                         wheel = self.callwheels[cpu]
1381                 except:
1382                         wheel = Callwheel(cpu)
1383                         self.callwheels[cpu] = wheel
1384                         self.sources.append(wheel)
1385                 return (wheel)
1386
1387         def findtd(self, td, pcomm):
1388                 for thread in self.threads:
1389                         if (thread.str == td and thread.name == pcomm):
1390                                 return thread
1391                 thread = Thread(td, pcomm)
1392                 self.threads.append(thread)
1393                 self.sources.append(thread)
1394                 return (thread)
1395
1396         def fixup(self):
1397                 for source in self.sources:
1398                         Padevent(source, -1, self.timestamp_l)
1399                         Padevent(source, -1, self.timestamp_f, last=1)
1400                         source.fixup()
1401                 self.sources.sort()
1402
1403 class SchedDisplay(Canvas):
1404         def __init__(self, master):
1405                 self.ratio = 1
1406                 self.ktrfile = None
1407                 self.sources = None
1408                 self.parent = master
1409                 self.bdheight = 10 
1410                 self.events = {}
1411
1412                 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1413                      scrollregion=(0, 0, 800, 500))
1414
1415         def setfile(self, ktrfile):
1416                 self.ktrfile = ktrfile
1417                 self.sources = ktrfile.sources
1418
1419                 # Compute a ratio to ensure that the file's timespan fits into
1420                 # 2^31.  Although python may handle larger values for X
1421                 # values, the Tk internals do not.
1422                 self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1
1423
1424         def draw(self):
1425                 ypos = 0
1426                 xsize = self.xsize()
1427                 for source in self.sources:
1428                         status.startup("Drawing " + source.name)
1429                         self.create_line(0, ypos, xsize, ypos,
1430                             width=1, fill="black", tags=("all",))
1431                         ypos += self.bdheight
1432                         ypos += source.ysize()
1433                         source.draw(self, ypos)
1434                         ypos += self.bdheight
1435                         try:
1436                                 self.tag_raise("point", "state")
1437                                 self.tag_lower("cpuinfo", "all")
1438                         except:
1439                                 pass
1440                 self.create_line(0, ypos, xsize, ypos,
1441                     width=1, fill="black", tags=("all",))
1442                 self.tag_bind("event", "<Enter>", self.mouseenter)
1443                 self.tag_bind("event", "<Leave>", self.mouseexit)
1444                 self.tag_bind("event", "<Button-1>", self.mousepress)
1445                 self.bind("<Button-4>", self.wheelup)
1446                 self.bind("<Button-5>", self.wheeldown)
1447
1448         def mouseenter(self, event):
1449                 item, = self.find_withtag(CURRENT)
1450                 event = self.events[item]
1451                 event.mouseenter(self, item)
1452
1453         def mouseexit(self, event):
1454                 item, = self.find_withtag(CURRENT)
1455                 event = self.events[item]
1456                 event.mouseexit(self, item)
1457
1458         def mousepress(self, event):
1459                 item, = self.find_withtag(CURRENT)
1460                 event = self.events[item]
1461                 event.mousepress(self, item)
1462
1463         def wheeldown(self, event):
1464                 self.parent.display_yview("scroll", 1, "units")
1465
1466         def wheelup(self, event):
1467                 self.parent.display_yview("scroll", -1, "units")
1468
1469         def drawnames(self, canvas):
1470                 status.startup("Drawing names")
1471                 ypos = 0
1472                 canvas.configure(scrollregion=(0, 0,
1473                     canvas["width"], self.ysize()))
1474                 for source in self.sources:
1475                         canvas.create_line(0, ypos, canvas["width"], ypos,
1476                             width=1, fill="black", tags=("all",))
1477                         ypos += self.bdheight
1478                         ypos += source.ysize()
1479                         source.drawname(canvas, ypos)
1480                         ypos += self.bdheight
1481                 canvas.create_line(0, ypos, canvas["width"], ypos,
1482                     width=1, fill="black", tags=("all",))
1483
1484         def xsize(self):
1485                 return ((self.ktrfile.timespan() / self.ratio) + 20)
1486
1487         def ysize(self):
1488                 ysize = 0
1489                 for source in self.sources:
1490                         ysize += source.ysize() + (self.bdheight * 2)
1491                 return (ysize)
1492
1493         def scaleset(self, ratio):
1494                 if (self.ktrfile == None):
1495                         return
1496                 oldratio = self.ratio
1497                 xstart, ystart = self.xview()
1498                 length = (float(self["width"]) / self.xsize())
1499                 middle = xstart + (length / 2)
1500
1501                 self.ratio = ratio
1502                 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1503                 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1504
1505                 length = (float(self["width"]) / self.xsize())
1506                 xstart = middle - (length / 2)
1507                 self.xview_moveto(xstart)
1508
1509         def scaleget(self):
1510                 return self.ratio
1511
1512         def setcolor(self, tag, color):
1513                 self.itemconfigure(tag, state="normal", fill=color)
1514
1515         def hide(self, tag):
1516                 self.itemconfigure(tag, state="hidden")
1517
1518 class GraphMenu(Frame):
1519         def __init__(self, master):
1520                 Frame.__init__(self, master, bd=2, relief=RAISED)
1521                 self.view = Menubutton(self, text="Configure")
1522                 self.viewmenu = Menu(self.view, tearoff=0)
1523                 self.viewmenu.add_command(label="Events",
1524                     command=self.econf)
1525                 self.view["menu"] = self.viewmenu
1526                 self.view.pack(side=LEFT)
1527
1528         def econf(self):
1529                 EventConfigure()
1530
1531
1532 class SchedGraph(Frame):
1533         def __init__(self, master):
1534                 Frame.__init__(self, master)
1535                 self.menu = None
1536                 self.names = None
1537                 self.display = None
1538                 self.scale = None
1539                 self.status = None
1540                 self.pack(expand=1, fill="both")
1541                 self.buildwidgets()
1542                 self.layout()
1543                 self.draw(sys.argv[1])
1544
1545         def buildwidgets(self):
1546                 global status
1547                 self.menu = GraphMenu(self)
1548                 self.display = SchedDisplay(self)
1549                 self.names = Canvas(self,
1550                     width=120, height=self.display["height"],
1551                     bg='grey', scrollregion=(0, 0, 50, 100))
1552                 self.scale = Scaler(self, self.display)
1553                 status = self.status = Status(self)
1554                 self.scrollY = Scrollbar(self, orient="vertical",
1555                     command=self.display_yview)
1556                 self.display.scrollX = Scrollbar(self, orient="horizontal",
1557                     command=self.display.xview)
1558                 self.display["xscrollcommand"] = self.display.scrollX.set
1559                 self.display["yscrollcommand"] = self.scrollY.set
1560                 self.names["yscrollcommand"] = self.scrollY.set
1561
1562         def layout(self):
1563                 self.columnconfigure(1, weight=1)
1564                 self.rowconfigure(1, weight=1)
1565                 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1566                 self.names.grid(row=1, column=0, sticky=N+S)
1567                 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1568                 self.scrollY.grid(row=1, column=2, sticky=N+S)
1569                 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1570                     sticky=E+W)
1571                 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1572                 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1573
1574         def draw(self, file):
1575                 self.master.update()
1576                 ktrfile = KTRFile(file)
1577                 self.display.setfile(ktrfile)
1578                 self.display.drawnames(self.names)
1579                 self.display.draw()
1580                 self.scale.set(250000)
1581                 self.display.xview_moveto(0)
1582
1583         def display_yview(self, *args):
1584                 self.names.yview(*args)
1585                 self.display.yview(*args)
1586
1587         def setcolor(self, tag, color):
1588                 self.display.setcolor(tag, color)
1589
1590         def hide(self, tag):
1591                 self.display.hide(tag)
1592
1593 if (len(sys.argv) != 2):
1594         print "usage:", sys.argv[0], "<ktr file>"
1595         sys.exit(1)
1596
1597 root = Tk()
1598 root.title("Scheduler Graph")
1599 graph = SchedGraph(root)
1600 root.mainloop()