1 #!/usr/local/bin/python
3 # Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org>
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice unmodified, this list of conditions, and the following
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.
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.
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.
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'
59 # 1) Add a per-thread summary display
60 # 2) Add bounding box style zoom.
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
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
81 us = ticksps / 1000000
84 return (str(ticks) + "us")
87 return (str(ticks) + "ms")
89 return (str(ticks) + "s")
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,
97 self.label = Label(self, text="Ticks per pixel")
98 self.label.pack(side=LEFT)
99 self.scale.pack(fill="both", expand=1)
101 self.scale.set(target.scaleget())
104 def scaleset(self, value):
105 self.target.scaleset(int(value))
107 def set(self, value):
108 self.scale.set(value)
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)
118 self.label.config(text=str)
121 self.label.config(text="")
123 def startup(self, str):
127 class EventConf(Frame):
128 def __init__(self, master, name, color, enabled):
129 Frame.__init__(self, master)
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)
142 self.label = Label(self, text=self.name, anchor=W)
143 self.sample = Canvas(self, width=24, height=24,
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",
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)
166 def setcolor(self, color):
167 self.color.set(color)
168 self.sample.itemconfigure(self.rect, fill=color)
173 if (self.color_current != self.color.get()):
175 if (self.enabled_current != self.enabled.get()):
177 self.color_current = self.color.get()
178 self.enabled_current = self.enabled.get()
180 if (self.enabled_current):
181 graph.setcolor(self.name, self.color_current)
183 graph.hide(self.name)
186 graph.setcolor(self.name, self.color_current)
189 self.setcolor(self.color_current)
190 self.enabled.set(self.enabled_current)
193 self.setcolor(self.color_default)
194 self.enabled.set(self.enabled_default)
196 class EventConfigure(Toplevel):
198 Toplevel.__init__(self)
200 self.title("Event Configuration")
201 self.items = LabelFrame(self, text="Event Type")
202 self.buttons = Frame(self)
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)
209 for type in configtypes:
210 self.additem(type.name, type.color, type.enabled)
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)
218 def drawbuttons(self):
219 self.apply = Button(self.buttons, text="Apply",
221 self.revert = Button(self.buttons, text="Revert",
223 self.default = Button(self.buttons, text="Default",
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)
233 for item in self.types:
237 for item in self.types:
241 for item in self.types:
244 class EventView(Toplevel):
245 def __init__(self, event, canvas):
246 Toplevel.__init__(self)
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)
257 event.displayref(canvas)
258 self.bind("<Destroy>", self.destroycb)
260 def destroycb(self, event):
261 self.unbind("<Destroy>")
262 if (self.event != None):
263 self.event.displayunref(self.canvas)
267 def clearlabels(self):
268 for label in self.frame.grid_slaves():
271 def drawlabels(self):
273 labels = self.event.labels()
274 while (len(labels) < 7):
275 labels.append(("", "", 0))
277 name, value, linked = label
278 l = Label(self.frame, text=name, bd=1, width=15,
279 relief=SUNKEN, anchor=W)
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)
289 r.bind("<Button-1>", self.linkpress)
291 self.frame.columnconfigure(1, minsize=80)
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)
302 def newevent(self, event):
303 self.event.displayunref(self.canvas)
306 self.event.displayref(self.canvas)
310 EventView(self.event, self.canvas)
313 prev = self.event.prev()
316 while (prev.real == 0):
323 next = self.event.next()
326 while (next.real == 0):
332 def linkpress(self, wevent):
333 event = self.event.getlinked()
340 def __init__(self, source, cpu, timestamp, last=0):
343 self.timestamp = int(timestamp)
353 source.lastevent(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()
367 def textadd(self, tuple):
369 self.entries.append(tuple)
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)
378 def mouseenter(self, canvas, item):
379 self.displayref(canvas)
382 def mouseexit(self, canvas, item):
383 self.displayunref(canvas)
386 def mousepress(self, canvas, item):
387 EventView(self, canvas)
390 return self.source.eventat(self.idx + 1)
393 return self.source.eventat(self.idx - 1)
395 def displayref(self, canvas):
396 if (self.dispcnt == 0):
397 canvas.itemconfigure(self.item, width=2)
400 def displayunref(self, canvas):
402 if (self.dispcnt == 0):
403 canvas.itemconfigure(self.item, width=0)
404 canvas.tag_raise("point", "state")
407 return self.linked.findevent(self.timestamp)
409 class PointEvent(Event):
410 def __init__(self, thread, cpu, timestamp, last=0):
411 Event.__init__(self, thread, cpu, timestamp, last)
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
419 if (self.enabled == 0):
420 canvas.itemconfigure(l, state="hidden")
424 class StateEvent(Event):
425 def __init__(self, thread, cpu, timestamp, last=0):
426 Event.__init__(self, thread, cpu, timestamp, last)
432 def draw(self, canvas, xpos, ypos):
433 next = self.nextstate()
434 if (self.skipself == 1 or next == None):
436 while (self.skipnext):
440 next = next.nextstate()
444 self.duration = next.timestamp - self.timestamp
445 if (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
456 if (self.enabled == 0):
457 canvas.itemconfigure(l, state="hidden")
459 return (xpos + delta)
462 return " duration: " + ticks2sec(self.duration)
466 while (next != None and next.state == 0):
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)
483 def __init__(self, source, cpu, timestamp, count):
484 self.count = int(count)
485 Event.__init__(self, source, cpu, timestamp)
487 self.textadd(("count:", self.count, 0))
489 def draw(self, canvas, xpos, ypos):
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
499 if (self.enabled == 0):
500 canvas.itemconfigure(l, state="hidden")
501 return (xpos + delta)
504 return " count: " + str(self.count)
506 configtypes.append(Count)
508 class Running(StateEvent):
512 def __init__(self, thread, cpu, timestamp, prio):
513 StateEvent.__init__(self, thread, cpu, timestamp)
515 self.textadd(("prio:", self.prio, 0))
517 configtypes.append(Running)
519 class Idle(StateEvent):
523 def __init__(self, thread, cpu, timestamp, prio):
524 StateEvent.__init__(self, thread, cpu, timestamp)
526 self.textadd(("prio:", self.prio, 0))
528 configtypes.append(Idle)
530 class Yielding(StateEvent):
534 def __init__(self, thread, cpu, timestamp, prio):
535 StateEvent.__init__(self, thread, cpu, timestamp)
538 self.textadd(("prio:", self.prio, 0))
540 configtypes.append(Yielding)
542 class Swapped(StateEvent):
546 def __init__(self, thread, cpu, timestamp, prio):
547 StateEvent.__init__(self, thread, cpu, timestamp)
549 self.textadd(("prio:", self.prio, 0))
551 configtypes.append(Swapped)
553 class Suspended(StateEvent):
557 def __init__(self, thread, cpu, timestamp, prio):
558 StateEvent.__init__(self, thread, cpu, timestamp)
560 self.textadd(("prio:", self.prio, 0))
562 configtypes.append(Suspended)
564 class Iwait(StateEvent):
568 def __init__(self, thread, cpu, timestamp, prio):
569 StateEvent.__init__(self, thread, cpu, timestamp)
571 self.textadd(("prio:", self.prio, 0))
573 configtypes.append(Iwait)
575 class Preempted(StateEvent):
579 def __init__(self, thread, cpu, timestamp, prio, bythread):
580 StateEvent.__init__(self, thread, cpu, timestamp)
583 self.linked = bythread
584 self.textadd(("prio:", self.prio, 0))
585 self.textadd(("by thread:", self.linked.name, 1))
587 configtypes.append(Preempted)
589 class Sleep(StateEvent):
593 def __init__(self, thread, cpu, timestamp, prio, wmesg):
594 StateEvent.__init__(self, thread, cpu, timestamp)
597 self.textadd(("prio:", self.prio, 0))
598 self.textadd(("wmesg:", self.wmesg, 0))
601 statstr = StateEvent.stattxt(self)
602 statstr += " sleeping on: " + self.wmesg
605 configtypes.append(Sleep)
607 class Blocked(StateEvent):
611 def __init__(self, thread, cpu, timestamp, prio, lock):
612 StateEvent.__init__(self, thread, cpu, timestamp)
615 self.textadd(("prio:", self.prio, 0))
616 self.textadd(("lock:", self.lock, 0))
619 statstr = StateEvent.stattxt(self)
620 statstr += " blocked on: " + self.lock
623 configtypes.append(Blocked)
625 class KsegrpRunq(StateEvent):
629 def __init__(self, thread, cpu, timestamp, prio, bythread):
630 StateEvent.__init__(self, thread, cpu, timestamp)
632 self.linked = bythread
633 self.textadd(("prio:", self.prio, 0))
634 self.textadd(("by thread:", self.linked.name, 1))
636 configtypes.append(KsegrpRunq)
638 class Runq(StateEvent):
642 def __init__(self, thread, cpu, timestamp, prio, bythread):
643 StateEvent.__init__(self, thread, cpu, timestamp)
645 self.linked = bythread
646 self.textadd(("prio:", self.prio, 0))
647 self.textadd(("by thread:", self.linked.name, 1))
649 configtypes.append(Runq)
651 class Sched_exit_thread(StateEvent):
655 def __init__(self, thread, cpu, timestamp, prio):
656 StateEvent.__init__(self, thread, cpu, timestamp)
657 self.name = "sched_exit_thread"
659 self.textadd(("prio:", self.prio, 0))
661 configtypes.append(Sched_exit_thread)
663 class Sched_exit(StateEvent):
667 def __init__(self, thread, cpu, timestamp, prio):
668 StateEvent.__init__(self, thread, cpu, timestamp)
669 self.name = "sched_exit"
671 self.textadd(("prio:", self.prio, 0))
673 configtypes.append(Sched_exit)
675 class Padevent(StateEvent):
676 def __init__(self, thread, cpu, timestamp, last=0):
677 StateEvent.__init__(self, thread, cpu, timestamp, last)
681 def draw(self, canvas, xpos, ypos):
685 self.duration = next.timestamp - self.timestamp
686 delta = self.duration / canvas.ratio
687 return (xpos + delta)
689 class Tick(PointEvent):
693 def __init__(self, thread, cpu, timestamp, prio, stathz):
694 PointEvent.__init__(self, thread, cpu, timestamp)
696 self.textadd(("prio:", self.prio, 0))
698 configtypes.append(Tick)
700 class Prio(PointEvent):
704 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
705 PointEvent.__init__(self, thread, cpu, timestamp)
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))
714 self.textadd(("by thread:", self.linked.name, 0))
716 configtypes.append(Prio)
718 class Lend(PointEvent):
722 def __init__(self, thread, cpu, timestamp, prio, tothread):
723 PointEvent.__init__(self, thread, cpu, timestamp)
725 self.linked = tothread
726 self.textadd(("prio:", self.prio, 0))
727 self.textadd(("to thread:", self.linked.name, 1))
729 configtypes.append(Lend)
731 class Wokeup(PointEvent):
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))
740 configtypes.append(Wokeup)
743 def __init__(self, name):
752 def event(self, event):
753 self.events.insert(0, event)
755 def remove(self, event):
756 self.events.remove(event)
758 def lastevent(self, event):
759 self.events.append(event)
761 def draw(self, canvas, ypos):
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)
772 xpos = event.draw(canvas, xpos, ypos)
773 self.drawcpu(canvas, xpos, ypos)
775 def drawname(self, canvas, ypos):
776 ypos = ypos - (self.ysize() / 2)
777 canvas.create_text(10, ypos, anchor="w", text=self.name)
779 def drawcpu(self, canvas, xpos, ypos):
788 color = 'light green'
790 color = 'blanched almond'
794 color = 'light slate blue'
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"))
807 def eventat(self, i):
808 if (i >= len(self.events)):
810 event = self.events[i]
813 def findevent(self, timestamp):
814 for event in self.events:
815 if (event.timestamp >= timestamp and event.real):
819 class Thread(EventSource):
821 def __init__(self, td, pcomm):
822 EventSource.__init__(self, pcomm)
825 cnt = Thread.names[pcomm]
827 Thread.names[pcomm] = 0
829 Thread.names[pcomm] = cnt + 1
832 cnt = Thread.names[self.name]
836 Thread.names[self.name] = cnt
837 self.name += " td" + str(cnt)
842 class Counter(EventSource):
844 def __init__(self, name):
845 EventSource.__init__(self, name)
847 def event(self, event):
848 EventSource.event(self, event)
854 if (count > Counter.max):
864 return (self.ysize() / Counter.max)
868 def __init__(self, file):
869 self.timestamp_f = None
870 self.timestamp_l = None
881 print "first", self.timestamp_f, "last", self.timestamp_l
882 print "time span", self.timespan()
883 print "stathz", self.stathz
884 ticksps = self.ticksps()
885 print "Ticks per second", ticksps
887 def parse(self, file):
891 print "Can't open", file
894 ktrhdr = "\s*\d+\s+(\d+)\s+(\d+)\s+"
895 tdname = "(\S+)\(([^)]*)\)"
896 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
898 # XXX doesn't handle:
899 # 371 0 61628682318 mi_switch: 0xc075c070(swapper) prio 180 inhibit 2 wmesg ATA request done lock (null)
900 ktrstr = "mi_switch: " + tdname
901 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
902 switchout_re = re.compile(ktrhdr + ktrstr)
904 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
905 idled_re = re.compile(ktrhdr + ktrstr)
907 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
909 preempted_re = re.compile(ktrhdr + ktrstr)
911 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
912 switchin_re = re.compile(ktrhdr + ktrstr)
914 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
915 sched_add_re = re.compile(ktrhdr + ktrstr)
917 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
918 setrunqueue_re = re.compile(ktrhdr + ktrstr)
920 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
921 sched_rem_re = re.compile(ktrhdr + ktrstr)
923 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
924 sched_exit_thread_re = re.compile(ktrhdr + ktrstr)
926 ktrstr = "sched_exit: " + tdname + " prio (\d+)"
927 sched_exit_re = re.compile(ktrhdr + ktrstr)
929 ktrstr = "statclock: " + tdname + " prio (\d+)"
930 ktrstr += " stathz (\d+)"
931 sched_clock_re = re.compile(ktrhdr + ktrstr)
933 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
934 ktrstr += " newprio (\d+) by " + tdname
935 sched_prio_re = re.compile(ktrhdr + ktrstr)
937 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
938 cpuload2_re = re.compile(ktrhdr + "cpu (\d+) load: (\d+)")
939 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
941 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
942 critsec_re = re.compile(ktrhdr + ktrstr)
944 parsers = [[cpuload_re, self.cpuload],
945 [cpuload2_re, self.cpuload2],
946 [loadglobal_re, self.loadglobal],
947 [switchin_re, self.switchin],
948 [switchout_re, self.switchout],
949 [sched_add_re, self.sched_add],
950 [setrunqueue_re, self.sched_rem],
951 [sched_prio_re, self.sched_prio],
952 [preempted_re, self.preempted],
953 [sched_rem_re, self.sched_rem],
954 [sched_exit_thread_re, self.sched_exit_thread],
955 [sched_exit_re, self.sched_exit],
956 [sched_clock_re, self.sched_clock],
957 [critsec_re, self.critsec],
958 [idled_re, self.idled]]
962 for line in ifp.readlines():
964 if ((lineno % 1024) == 0):
965 status.startup("Parsing line " + str(lineno))
974 def checkstamp(self, cpu, timestamp):
975 timestamp = int(timestamp)
976 if (self.timestamp_f == None):
977 self.timestamp_f = timestamp;
978 if (self.timestamp_l != None and timestamp > self.timestamp_l):
980 self.timestamp_l = timestamp;
984 return (self.timestamp_f - self.timestamp_l);
987 return (self.timespan() / self.ticks[0]) * int(self.stathz)
989 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
990 TDI_SUSPENDED = 0x0001
991 TDI_SLEEPING = 0x0002
996 timestamp = self.checkstamp(cpu, timestamp)
999 inhibit = int(inhibit)
1000 thread = self.findtd(td, pcomm)
1001 if (inhibit & TDI_SWAPPED):
1002 Swapped(thread, cpu, timestamp, prio)
1003 elif (inhibit & TDI_SLEEPING):
1004 Sleep(thread, cpu, timestamp, prio, wmesg)
1005 elif (inhibit & TDI_LOCK):
1006 Blocked(thread, cpu, timestamp, prio, lock)
1007 elif (inhibit & TDI_IWAIT):
1008 Iwait(thread, cpu, timestamp, prio)
1009 elif (inhibit & TDI_SUSPENDED):
1010 Suspended(thread, cpu, timestamp, prio)
1011 elif (inhibit == 0):
1012 Yielding(thread, cpu, timestamp, prio)
1014 print "Unknown event", inhibit
1017 def idled(self, cpu, timestamp, td, pcomm, prio):
1018 timestamp = self.checkstamp(cpu, timestamp)
1019 if (timestamp == 0):
1021 thread = self.findtd(td, pcomm)
1022 Idle(thread, cpu, timestamp, prio)
1024 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1025 timestamp = self.checkstamp(cpu, timestamp)
1026 if (timestamp == 0):
1028 thread = self.findtd(td, pcomm)
1029 Preempted(thread, cpu, timestamp, prio,
1030 self.findtd(bytd, bypcomm))
1032 def switchin(self, cpu, timestamp, td, pcomm, prio):
1033 timestamp = self.checkstamp(cpu, timestamp)
1034 if (timestamp == 0):
1036 thread = self.findtd(td, pcomm)
1037 Running(thread, cpu, timestamp, prio)
1039 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1040 timestamp = self.checkstamp(cpu, timestamp)
1041 if (timestamp == 0):
1043 thread = self.findtd(td, pcomm)
1044 bythread = self.findtd(bytd, bypcomm)
1045 Runq(thread, cpu, timestamp, prio, bythread)
1046 Wokeup(bythread, cpu, timestamp, thread)
1048 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1049 timestamp = self.checkstamp(cpu, timestamp)
1050 if (timestamp == 0):
1052 thread = self.findtd(td, pcomm)
1053 KsegrpRunq(thread, cpu, timestamp, prio,
1054 self.findtd(bytd, bypcomm))
1056 def sched_exit_thread(self, cpu, timestamp, td, pcomm, prio):
1057 timestamp = self.checkstamp(cpu, timestamp)
1058 if (timestamp == 0):
1060 thread = self.findtd(td, pcomm)
1061 Sched_exit_thread(thread, cpu, timestamp, prio)
1063 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1064 timestamp = self.checkstamp(cpu, timestamp)
1065 if (timestamp == 0):
1067 thread = self.findtd(td, pcomm)
1068 Sched_exit(thread, cpu, timestamp, prio)
1070 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1071 timestamp = self.checkstamp(cpu, timestamp)
1072 if (timestamp == 0):
1074 self.stathz = stathz
1077 ticks = self.ticks[cpu]
1080 self.ticks[cpu] += 1
1081 thread = self.findtd(td, pcomm)
1082 Tick(thread, cpu, timestamp, prio, stathz)
1084 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1085 if (prio == newprio):
1087 timestamp = self.checkstamp(cpu, timestamp)
1088 if (timestamp == 0):
1090 thread = self.findtd(td, pcomm)
1091 bythread = self.findtd(bytd, bypcomm)
1092 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1093 Lend(bythread, cpu, timestamp, newprio, thread)
1095 def cpuload(self, cpu, timestamp, count):
1096 timestamp = self.checkstamp(cpu, timestamp)
1097 if (timestamp == 0):
1101 load = self.load[cpu]
1103 load = Counter("cpu" + str(cpu) + " load")
1104 self.load[cpu] = load
1105 self.sources.insert(0, load)
1106 Count(load, cpu, timestamp, count)
1108 def cpuload2(self, cpu, timestamp, ncpu, count):
1109 timestamp = self.checkstamp(cpu, timestamp)
1110 if (timestamp == 0):
1114 load = self.load[cpu]
1116 load = Counter("cpu" + str(cpu) + " load")
1117 self.load[cpu] = load
1118 self.sources.insert(0, load)
1119 Count(load, cpu, timestamp, count)
1121 def loadglobal(self, cpu, timestamp, count):
1122 timestamp = self.checkstamp(cpu, timestamp)
1123 if (timestamp == 0):
1127 load = self.load[cpu]
1129 load = Counter("CPU load")
1130 self.load[cpu] = load
1131 self.sources.insert(0, load)
1132 Count(load, cpu, timestamp, count)
1134 def critsec(self, cpu, timestamp, td, pcomm, to):
1135 timestamp = self.checkstamp(cpu, timestamp)
1136 if (timestamp == 0):
1140 crit = self.crit[cpu]
1142 crit = Counter("Critical Section")
1143 self.crit[cpu] = crit
1144 self.sources.insert(0, crit)
1145 Count(crit, cpu, timestamp, to)
1147 def findtd(self, td, pcomm):
1148 for thread in self.threads:
1149 if (thread.str == td and thread.name == pcomm):
1151 thread = Thread(td, pcomm)
1152 self.threads.append(thread)
1153 self.sources.append(thread)
1157 for source in self.sources:
1158 Padevent(source, -1, self.timestamp_l)
1159 Padevent(source, -1, self.timestamp_f, last=1)
1162 class SchedDisplay(Canvas):
1163 def __init__(self, master):
1170 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1171 scrollregion=(0, 0, 800, 500))
1173 def setfile(self, ktrfile):
1174 self.ktrfile = ktrfile
1175 self.sources = ktrfile.sources
1179 xsize = self.xsize()
1180 for source in self.sources:
1181 status.startup("Drawing " + source.name)
1182 self.create_line(0, ypos, xsize, ypos,
1183 width=1, fill="black", tags=("all",))
1184 ypos += self.bdheight
1185 ypos += source.ysize()
1186 source.draw(self, ypos)
1187 ypos += self.bdheight
1189 self.tag_raise("point", "state")
1190 self.tag_lower("cpuinfo", "all")
1193 self.create_line(0, ypos, xsize, ypos,
1194 width=1, fill="black", tags=("all",))
1195 self.tag_bind("event", "<Enter>", self.mouseenter)
1196 self.tag_bind("event", "<Leave>", self.mouseexit)
1197 self.tag_bind("event", "<Button-1>", self.mousepress)
1199 def mouseenter(self, event):
1200 item, = self.find_withtag(CURRENT)
1201 event = self.events[item]
1202 event.mouseenter(self, item)
1204 def mouseexit(self, event):
1205 item, = self.find_withtag(CURRENT)
1206 event = self.events[item]
1207 event.mouseexit(self, item)
1209 def mousepress(self, event):
1210 item, = self.find_withtag(CURRENT)
1211 event = self.events[item]
1212 event.mousepress(self, item)
1214 def drawnames(self, canvas):
1215 status.startup("Drawing names")
1217 canvas.configure(scrollregion=(0, 0,
1218 canvas["width"], self.ysize()))
1219 for source in self.sources:
1220 canvas.create_line(0, ypos, canvas["width"], ypos,
1221 width=1, fill="black", tags=("all",))
1222 ypos += self.bdheight
1223 ypos += source.ysize()
1224 source.drawname(canvas, ypos)
1225 ypos += self.bdheight
1226 canvas.create_line(0, ypos, canvas["width"], ypos,
1227 width=1, fill="black", tags=("all",))
1230 return ((self.ktrfile.timespan() / self.ratio) + 20)
1234 for source in self.sources:
1235 ysize += source.ysize() + (self.bdheight * 2)
1238 def scaleset(self, ratio):
1239 if (self.ktrfile == None):
1241 oldratio = self.ratio
1242 xstart, ystart = self.xview()
1243 length = (float(self["width"]) / self.xsize())
1244 middle = xstart + (length / 2)
1247 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1248 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1250 length = (float(self["width"]) / self.xsize())
1251 xstart = middle - (length / 2)
1252 self.xview_moveto(xstart)
1257 def setcolor(self, tag, color):
1258 self.itemconfigure(tag, state="normal", fill=color)
1260 def hide(self, tag):
1261 self.itemconfigure(tag, state="hidden")
1263 class GraphMenu(Frame):
1264 def __init__(self, master):
1265 Frame.__init__(self, master, bd=2, relief=RAISED)
1266 self.view = Menubutton(self, text="Configure")
1267 self.viewmenu = Menu(self.view, tearoff=0)
1268 self.viewmenu.add_command(label="Events",
1270 self.view["menu"] = self.viewmenu
1271 self.view.pack(side=LEFT)
1277 class SchedGraph(Frame):
1278 def __init__(self, master):
1279 Frame.__init__(self, master)
1285 self.pack(expand=1, fill="both")
1288 self.draw(sys.argv[1])
1290 def buildwidgets(self):
1292 self.menu = GraphMenu(self)
1293 self.display = SchedDisplay(self)
1294 self.names = Canvas(self,
1295 width=100, height=self.display["height"],
1296 bg='grey', scrollregion=(0, 0, 50, 100))
1297 self.scale = Scaler(self, self.display)
1298 status = self.status = Status(self)
1299 self.scrollY = Scrollbar(self, orient="vertical",
1300 command=self.display_yview)
1301 self.display.scrollX = Scrollbar(self, orient="horizontal",
1302 command=self.display.xview)
1303 self.display["xscrollcommand"] = self.display.scrollX.set
1304 self.display["yscrollcommand"] = self.scrollY.set
1305 self.names["yscrollcommand"] = self.scrollY.set
1308 self.columnconfigure(1, weight=1)
1309 self.rowconfigure(1, weight=1)
1310 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1311 self.names.grid(row=1, column=0, sticky=N+S)
1312 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1313 self.scrollY.grid(row=1, column=2, sticky=N+S)
1314 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1316 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1317 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1319 def draw(self, file):
1320 self.master.update()
1321 ktrfile = KTRFile(file)
1322 self.display.setfile(ktrfile)
1323 self.display.drawnames(self.names)
1325 self.scale.set(250000)
1326 self.display.xview_moveto(0)
1328 def display_yview(self, *args):
1329 self.names.yview(*args)
1330 self.display.yview(*args)
1332 def setcolor(self, tag, color):
1333 self.display.setcolor(tag, color)
1335 def hide(self, tag):
1336 self.display.hide(tag)
1338 if (len(sys.argv) != 2):
1339 print "usage:", sys.argv[0], "<ktr file>"
1343 root.title("Scheduler Graph")
1344 graph = SchedGraph(root)