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_first = {}
870 self.timestamp_last = {}
871 self.timestamp_adjust = {}
872 self.timestamp_f = None
873 self.timestamp_l = None
884 print "first", self.timestamp_f, "last", self.timestamp_l
885 print "time span", self.timespan()
886 print "stathz", self.stathz
887 ticksps = self.ticksps()
888 print "Ticks per second", ticksps
890 def parse(self, file):
894 print "Can't open", file
897 ktrhdr = "\s*\d+\s+(\d+)\s+(\d+)\s+"
898 tdname = "(\S+)\(([^)]*)\)"
899 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
901 # XXX doesn't handle:
902 # 371 0 61628682318 mi_switch: 0xc075c070(swapper) prio 180 inhibit 2 wmesg ATA request done lock (null)
903 ktrstr = "mi_switch: " + tdname
904 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
905 switchout_re = re.compile(ktrhdr + ktrstr)
907 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
908 idled_re = re.compile(ktrhdr + ktrstr)
910 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
912 preempted_re = re.compile(ktrhdr + ktrstr)
914 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
915 switchin_re = re.compile(ktrhdr + ktrstr)
917 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
918 sched_add_re = re.compile(ktrhdr + ktrstr)
920 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
921 setrunqueue_re = re.compile(ktrhdr + ktrstr)
923 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
924 sched_rem_re = re.compile(ktrhdr + ktrstr)
926 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
927 sched_exit_thread_re = re.compile(ktrhdr + ktrstr)
929 ktrstr = "sched_exit: " + tdname + " prio (\d+)"
930 sched_exit_re = re.compile(ktrhdr + ktrstr)
932 ktrstr = "statclock: " + tdname + " prio (\d+)"
933 ktrstr += " stathz (\d+)"
934 sched_clock_re = re.compile(ktrhdr + ktrstr)
936 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
937 ktrstr += " newprio (\d+) by " + tdname
938 sched_prio_re = re.compile(ktrhdr + ktrstr)
940 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
941 cpuload2_re = re.compile(ktrhdr + "cpu (\d+) load: (\d+)")
942 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
944 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
945 critsec_re = re.compile(ktrhdr + ktrstr)
947 parsers = [[cpuload_re, self.cpuload],
948 [cpuload2_re, self.cpuload2],
949 [loadglobal_re, self.loadglobal],
950 [switchin_re, self.switchin],
951 [switchout_re, self.switchout],
952 [sched_add_re, self.sched_add],
953 [setrunqueue_re, self.sched_rem],
954 [sched_prio_re, self.sched_prio],
955 [preempted_re, self.preempted],
956 [sched_rem_re, self.sched_rem],
957 [sched_exit_thread_re, self.sched_exit_thread],
958 [sched_exit_re, self.sched_exit],
959 [sched_clock_re, self.sched_clock],
960 [critsec_re, self.critsec],
961 [idled_re, self.idled]]
965 lines = ifp.readlines()
966 self.synchstamp(lines)
969 if ((lineno % 1024) == 0):
970 status.startup("Parsing line " + str(lineno))
979 def synchstamp(self, lines):
980 status.startup("Rationalizing Timestamps")
981 tstamp_re = re.compile("\s*\d+\s+(\d+)\s+(\d+)\s+.*")
983 m = tstamp_re.match(line)
985 self.addstamp(*m.groups())
987 self.monostamp(lines)
990 def monostamp(self, lines):
992 tstamp_re = re.compile("\s*\d+\s+(\d+)\s+(\d+)\s+.*")
994 m = tstamp_re.match(line)
997 (cpu, timestamp) = m.groups()
998 timestamp = int(timestamp)
1000 timestamp -= self.timestamp_adjust[cpu]
1001 if (laststamp != None and timestamp > laststamp):
1002 self.timestamp_adjust[cpu] += timestamp - laststamp
1003 laststamp = timestamp
1005 def addstamp(self, cpu, timestamp):
1006 timestamp = int(timestamp)
1009 if (timestamp > self.timestamp_first[cpu]):
1012 self.timestamp_first[cpu] = timestamp
1013 self.timestamp_last[cpu] = timestamp
1015 def pickstamp(self):
1016 base = self.timestamp_last[0]
1017 for i in range(0, len(self.timestamp_last)):
1018 if (self.timestamp_last[i] < base):
1019 base = self.timestamp_last[i]
1021 print "Adjusting to base stamp", base
1022 for i in range(0, len(self.timestamp_last)):
1023 self.timestamp_adjust[i] = self.timestamp_last[i] - base;
1024 print "CPU ", i, "adjust by ", self.timestamp_adjust[i]
1026 self.timestamp_f = 0
1027 for i in range(0, len(self.timestamp_first)):
1028 first = self.timestamp_first[i] - self.timestamp_adjust[i]
1029 if (first > self.timestamp_f):
1030 self.timestamp_f = first
1032 self.timestamp_l = 0
1033 for i in range(0, len(self.timestamp_last)):
1034 last = self.timestamp_last[i] - self.timestamp_adjust[i]
1035 if (last > self.timestamp_l):
1036 self.timestamp_l = last
1039 def checkstamp(self, cpu, timestamp):
1041 timestamp = int(timestamp)
1042 if (timestamp > self.timestamp_first[cpu]):
1043 print "Bad timestamp on line ", lineno, " (", timestamp, " > ", self.timestamp_first[cpu], ")"
1045 timestamp -= self.timestamp_adjust[cpu]
1049 return (self.timestamp_f - self.timestamp_l);
1052 return (self.timespan() / self.ticks[0]) * int(self.stathz)
1054 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
1055 TDI_SUSPENDED = 0x0001
1056 TDI_SLEEPING = 0x0002
1057 TDI_SWAPPED = 0x0004
1061 timestamp = self.checkstamp(cpu, timestamp)
1062 if (timestamp == 0):
1064 inhibit = int(inhibit)
1065 thread = self.findtd(td, pcomm)
1066 if (inhibit & TDI_SWAPPED):
1067 Swapped(thread, cpu, timestamp, prio)
1068 elif (inhibit & TDI_SLEEPING):
1069 Sleep(thread, cpu, timestamp, prio, wmesg)
1070 elif (inhibit & TDI_LOCK):
1071 Blocked(thread, cpu, timestamp, prio, lock)
1072 elif (inhibit & TDI_IWAIT):
1073 Iwait(thread, cpu, timestamp, prio)
1074 elif (inhibit & TDI_SUSPENDED):
1075 Suspended(thread, cpu, timestamp, prio)
1076 elif (inhibit == 0):
1077 Yielding(thread, cpu, timestamp, prio)
1079 print "Unknown event", inhibit
1082 def idled(self, cpu, timestamp, td, pcomm, prio):
1083 timestamp = self.checkstamp(cpu, timestamp)
1084 if (timestamp == 0):
1086 thread = self.findtd(td, pcomm)
1087 Idle(thread, cpu, timestamp, prio)
1089 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1090 timestamp = self.checkstamp(cpu, timestamp)
1091 if (timestamp == 0):
1093 thread = self.findtd(td, pcomm)
1094 Preempted(thread, cpu, timestamp, prio,
1095 self.findtd(bytd, bypcomm))
1097 def switchin(self, cpu, timestamp, td, pcomm, prio):
1098 timestamp = self.checkstamp(cpu, timestamp)
1099 if (timestamp == 0):
1101 thread = self.findtd(td, pcomm)
1102 Running(thread, cpu, timestamp, prio)
1104 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1105 timestamp = self.checkstamp(cpu, timestamp)
1106 if (timestamp == 0):
1108 thread = self.findtd(td, pcomm)
1109 bythread = self.findtd(bytd, bypcomm)
1110 Runq(thread, cpu, timestamp, prio, bythread)
1111 Wokeup(bythread, cpu, timestamp, thread)
1113 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1114 timestamp = self.checkstamp(cpu, timestamp)
1115 if (timestamp == 0):
1117 thread = self.findtd(td, pcomm)
1118 KsegrpRunq(thread, cpu, timestamp, prio,
1119 self.findtd(bytd, bypcomm))
1121 def sched_exit_thread(self, cpu, timestamp, td, pcomm, prio):
1122 timestamp = self.checkstamp(cpu, timestamp)
1123 if (timestamp == 0):
1125 thread = self.findtd(td, pcomm)
1126 Sched_exit_thread(thread, cpu, timestamp, prio)
1128 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1129 timestamp = self.checkstamp(cpu, timestamp)
1130 if (timestamp == 0):
1132 thread = self.findtd(td, pcomm)
1133 Sched_exit(thread, cpu, timestamp, prio)
1135 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1136 timestamp = self.checkstamp(cpu, timestamp)
1137 if (timestamp == 0):
1139 self.stathz = stathz
1142 ticks = self.ticks[cpu]
1145 self.ticks[cpu] += 1
1146 thread = self.findtd(td, pcomm)
1147 Tick(thread, cpu, timestamp, prio, stathz)
1149 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1150 if (prio == newprio):
1152 timestamp = self.checkstamp(cpu, timestamp)
1153 if (timestamp == 0):
1155 thread = self.findtd(td, pcomm)
1156 bythread = self.findtd(bytd, bypcomm)
1157 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1158 Lend(bythread, cpu, timestamp, newprio, thread)
1160 def cpuload(self, cpu, timestamp, count):
1161 timestamp = self.checkstamp(cpu, timestamp)
1162 if (timestamp == 0):
1166 load = self.load[cpu]
1168 load = Counter("cpu" + str(cpu) + " load")
1169 self.load[cpu] = load
1170 self.sources.insert(0, load)
1171 Count(load, cpu, timestamp, count)
1173 def cpuload2(self, cpu, timestamp, ncpu, count):
1174 timestamp = self.checkstamp(cpu, timestamp)
1175 if (timestamp == 0):
1179 load = self.load[cpu]
1181 load = Counter("cpu" + str(cpu) + " load")
1182 self.load[cpu] = load
1183 self.sources.insert(0, load)
1184 Count(load, cpu, timestamp, count)
1186 def loadglobal(self, cpu, timestamp, count):
1187 timestamp = self.checkstamp(cpu, timestamp)
1188 if (timestamp == 0):
1192 load = self.load[cpu]
1194 load = Counter("CPU load")
1195 self.load[cpu] = load
1196 self.sources.insert(0, load)
1197 Count(load, cpu, timestamp, count)
1199 def critsec(self, cpu, timestamp, td, pcomm, to):
1200 timestamp = self.checkstamp(cpu, timestamp)
1201 if (timestamp == 0):
1205 crit = self.crit[cpu]
1207 crit = Counter("Critical Section")
1208 self.crit[cpu] = crit
1209 self.sources.insert(0, crit)
1210 Count(crit, cpu, timestamp, to)
1212 def findtd(self, td, pcomm):
1213 for thread in self.threads:
1214 if (thread.str == td and thread.name == pcomm):
1216 thread = Thread(td, pcomm)
1217 self.threads.append(thread)
1218 self.sources.append(thread)
1222 for source in self.sources:
1223 Padevent(source, -1, self.timestamp_l)
1224 Padevent(source, -1, self.timestamp_f, last=1)
1227 class SchedDisplay(Canvas):
1228 def __init__(self, master):
1235 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1236 scrollregion=(0, 0, 800, 500))
1238 def setfile(self, ktrfile):
1239 self.ktrfile = ktrfile
1240 self.sources = ktrfile.sources
1244 xsize = self.xsize()
1245 for source in self.sources:
1246 status.startup("Drawing " + source.name)
1247 self.create_line(0, ypos, xsize, ypos,
1248 width=1, fill="black", tags=("all",))
1249 ypos += self.bdheight
1250 ypos += source.ysize()
1251 source.draw(self, ypos)
1252 ypos += self.bdheight
1254 self.tag_raise("point", "state")
1255 self.tag_lower("cpuinfo", "all")
1258 self.create_line(0, ypos, xsize, ypos,
1259 width=1, fill="black", tags=("all",))
1260 self.tag_bind("event", "<Enter>", self.mouseenter)
1261 self.tag_bind("event", "<Leave>", self.mouseexit)
1262 self.tag_bind("event", "<Button-1>", self.mousepress)
1264 def mouseenter(self, event):
1265 item, = self.find_withtag(CURRENT)
1266 event = self.events[item]
1267 event.mouseenter(self, item)
1269 def mouseexit(self, event):
1270 item, = self.find_withtag(CURRENT)
1271 event = self.events[item]
1272 event.mouseexit(self, item)
1274 def mousepress(self, event):
1275 item, = self.find_withtag(CURRENT)
1276 event = self.events[item]
1277 event.mousepress(self, item)
1279 def drawnames(self, canvas):
1280 status.startup("Drawing names")
1282 canvas.configure(scrollregion=(0, 0,
1283 canvas["width"], self.ysize()))
1284 for source in self.sources:
1285 canvas.create_line(0, ypos, canvas["width"], ypos,
1286 width=1, fill="black", tags=("all",))
1287 ypos += self.bdheight
1288 ypos += source.ysize()
1289 source.drawname(canvas, ypos)
1290 ypos += self.bdheight
1291 canvas.create_line(0, ypos, canvas["width"], ypos,
1292 width=1, fill="black", tags=("all",))
1295 return ((self.ktrfile.timespan() / self.ratio) + 20)
1299 for source in self.sources:
1300 ysize += source.ysize() + (self.bdheight * 2)
1303 def scaleset(self, ratio):
1304 if (self.ktrfile == None):
1306 oldratio = self.ratio
1307 xstart, ystart = self.xview()
1308 length = (float(self["width"]) / self.xsize())
1309 middle = xstart + (length / 2)
1312 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1313 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1315 length = (float(self["width"]) / self.xsize())
1316 xstart = middle - (length / 2)
1317 self.xview_moveto(xstart)
1322 def setcolor(self, tag, color):
1323 self.itemconfigure(tag, state="normal", fill=color)
1325 def hide(self, tag):
1326 self.itemconfigure(tag, state="hidden")
1328 class GraphMenu(Frame):
1329 def __init__(self, master):
1330 Frame.__init__(self, master, bd=2, relief=RAISED)
1331 self.view = Menubutton(self, text="Configure")
1332 self.viewmenu = Menu(self.view, tearoff=0)
1333 self.viewmenu.add_command(label="Events",
1335 self.view["menu"] = self.viewmenu
1336 self.view.pack(side=LEFT)
1342 class SchedGraph(Frame):
1343 def __init__(self, master):
1344 Frame.__init__(self, master)
1350 self.pack(expand=1, fill="both")
1353 self.draw(sys.argv[1])
1355 def buildwidgets(self):
1357 self.menu = GraphMenu(self)
1358 self.display = SchedDisplay(self)
1359 self.names = Canvas(self,
1360 width=100, height=self.display["height"],
1361 bg='grey', scrollregion=(0, 0, 50, 100))
1362 self.scale = Scaler(self, self.display)
1363 status = self.status = Status(self)
1364 self.scrollY = Scrollbar(self, orient="vertical",
1365 command=self.display_yview)
1366 self.display.scrollX = Scrollbar(self, orient="horizontal",
1367 command=self.display.xview)
1368 self.display["xscrollcommand"] = self.display.scrollX.set
1369 self.display["yscrollcommand"] = self.scrollY.set
1370 self.names["yscrollcommand"] = self.scrollY.set
1373 self.columnconfigure(1, weight=1)
1374 self.rowconfigure(1, weight=1)
1375 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1376 self.names.grid(row=1, column=0, sticky=N+S)
1377 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1378 self.scrollY.grid(row=1, column=2, sticky=N+S)
1379 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1381 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1382 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1384 def draw(self, file):
1385 self.master.update()
1386 ktrfile = KTRFile(file)
1387 self.display.setfile(ktrfile)
1388 self.display.drawnames(self.names)
1390 self.scale.set(250000)
1391 self.display.xview_moveto(0)
1393 def display_yview(self, *args):
1394 self.names.yview(*args)
1395 self.display.yview(*args)
1397 def setcolor(self, tag, color):
1398 self.display.setcolor(tag, color)
1400 def hide(self, tag):
1401 self.display.hide(tag)
1403 if (len(sys.argv) != 2):
1404 print "usage:", sys.argv[0], "<ktr file>"
1408 root.title("Scheduler Graph")
1409 graph = SchedGraph(root)