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.
35 # - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF
36 # - It is encouraged to increase KTR_ENTRIES size to 32768 to gather
37 # enough information for analysis.
38 # - Rebuild kernel with proper changes to KERNCONF and boot new kernel.
39 # - Run your workload to be profiled.
40 # - While the workload is continuing (i.e. before it finishes), disable
41 # KTR tracing by setting 'sysctl debug.ktr.mask=0'. This is necessary
42 # to avoid a race condition while running ktrdump, i.e. the KTR ring buffer
43 # will cycle a bit while ktrdump runs, and this confuses schedgraph because
44 # the timestamps appear to go backwards at some point. Stopping KTR logging
45 # while the workload is still running is to avoid wasting log entries on
46 # "idle" time at the end.
47 # - Dump the trace to a file: 'ktrdump -ct > ktr.out'
48 # - Run the python script: 'python schedgraph.py ktr.out'
51 # 1) Add a per-thread summary display
52 # 2) Add bounding box style zoom.
54 # 4) Implement some sorting mechanism.
56 # BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of
57 # colours to represent them ;-)
58 # 2) Extremely short traces may cause a crash because the code
59 # assumes there is always at least one stathz entry logged, and
60 # the number of such events is used as a denominator
67 us = ticksps / 1000000
70 return (str(ticks) + "us")
73 return (str(ticks) + "ms")
75 return (str(ticks) + "s")
78 def __init__(self, master, target):
79 Frame.__init__(self, master)
80 self.scale = Scale(self, command=self.scaleset,
81 from_=1000, to_=10000000, orient=HORIZONTAL,
83 self.label = Label(self, text="Ticks per pixel")
84 self.label.pack(side=LEFT)
85 self.scale.pack(fill="both", expand=1)
87 self.scale.set(target.scaleget())
90 def scaleset(self, value):
91 self.target.scaleset(int(value))
97 def __init__(self, master):
98 Frame.__init__(self, master)
99 self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
100 self.label.pack(fill="both", expand=1)
104 self.label.config(text=str)
107 self.label.config(text="")
109 def startup(self, str):
113 class EventConf(Frame):
114 def __init__(self, master, name, color, enabled):
115 Frame.__init__(self, master)
117 self.color = StringVar()
118 self.color_default = color
119 self.color_current = color
120 self.color.set(color)
121 self.enabled = IntVar()
122 self.enabled_default = enabled
123 self.enabled_current = enabled
124 self.enabled.set(enabled)
128 self.label = Label(self, text=self.name, anchor=W)
129 self.sample = Canvas(self, width=24, height=24,
131 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
132 fill=self.color.get())
133 self.list = OptionMenu(self, self.color,
134 "dark red", "red", "pink",
135 "dark orange", "orange",
136 "yellow", "light yellow",
137 "dark green", "green", "light green",
138 "dark blue", "blue", "light blue",
139 "dark violet", "violet", "purple",
140 "dark grey", "light grey",
142 command=self.setcolor)
143 self.checkbox = Checkbutton(self, text="enabled",
144 variable=self.enabled)
145 self.label.grid(row=0, column=0, sticky=E+W)
146 self.sample.grid(row=0, column=1)
147 self.list.grid(row=0, column=2, sticky=E+W)
148 self.checkbox.grid(row=0, column=3)
149 self.columnconfigure(0, weight=1)
150 self.columnconfigure(2, minsize=110)
152 def setcolor(self, color):
153 self.color.set(color)
154 self.sample.itemconfigure(self.rect, fill=color)
159 if (self.color_current != self.color.get()):
161 if (self.enabled_current != self.enabled.get()):
163 self.color_current = self.color.get()
164 self.enabled_current = self.enabled.get()
166 if (self.enabled_current):
167 graph.setcolor(self.name, self.color_current)
169 graph.hide(self.name)
172 graph.setcolor(self.name, self.color_current)
175 self.setcolor(self.color_current)
176 self.enabled.set(self.enabled_current)
179 self.setcolor(self.color_default)
180 self.enabled.set(self.enabled_default)
182 class EventConfigure(Toplevel):
184 Toplevel.__init__(self)
186 self.title("Event Configuration")
187 self.items = LabelFrame(self, text="Event Type")
188 self.buttons = Frame(self)
190 self.items.grid(row=0, column=0, sticky=E+W)
191 self.columnconfigure(0, weight=1)
192 self.buttons.grid(row=1, column=0, sticky=E+W)
195 for type in configtypes:
196 self.additem(type.name, type.color, type.enabled)
198 def additem(self, name, color, enabled=1):
199 item = EventConf(self.items, name, color, enabled)
200 self.types.append(item)
201 item.grid(row=self.irow, column=0, sticky=E+W)
204 def drawbuttons(self):
205 self.apply = Button(self.buttons, text="Apply",
207 self.revert = Button(self.buttons, text="Revert",
209 self.default = Button(self.buttons, text="Default",
211 self.apply.grid(row=0, column=0, sticky=E+W)
212 self.revert.grid(row=0, column=1, sticky=E+W)
213 self.default.grid(row=0, column=2, sticky=E+W)
214 self.buttons.columnconfigure(0, weight=1)
215 self.buttons.columnconfigure(1, weight=1)
216 self.buttons.columnconfigure(2, weight=1)
219 for item in self.types:
223 for item in self.types:
227 for item in self.types:
230 class EventView(Toplevel):
231 def __init__(self, event, canvas):
232 Toplevel.__init__(self)
236 self.frame = Frame(self)
237 self.frame.grid(row=0, column=0, sticky=N+S+E+W)
238 self.buttons = Frame(self)
239 self.buttons.grid(row=1, column=0, sticky=E+W)
243 event.displayref(canvas)
244 self.bind("<Destroy>", self.destroycb)
246 def destroycb(self, event):
247 self.unbind("<Destroy>")
248 if (self.event != None):
249 self.event.displayunref(self.canvas)
253 def clearlabels(self):
254 for label in self.frame.grid_slaves():
257 def drawlabels(self):
259 labels = self.event.labels()
260 while (len(labels) < 7):
261 labels.append(("", "", 0))
263 name, value, linked = label
264 l = Label(self.frame, text=name, bd=1, width=15,
265 relief=SUNKEN, anchor=W)
270 r = Label(self.frame, text=value, bd=1,
271 relief=SUNKEN, anchor=W, fg=fgcolor)
272 l.grid(row=ypos, column=0, sticky=E+W)
273 r.grid(row=ypos, column=1, sticky=E+W)
275 r.bind("<Button-1>", self.linkpress)
277 self.frame.columnconfigure(1, minsize=80)
279 def drawbuttons(self):
280 self.back = Button(self.buttons, text="<", command=self.bpress)
281 self.forw = Button(self.buttons, text=">", command=self.fpress)
282 self.new = Button(self.buttons, text="new", command=self.npress)
283 self.back.grid(row=0, column=0, sticky=E+W)
284 self.forw.grid(row=0, column=1, sticky=E+W)
285 self.new.grid(row=0, column=2, sticky=E+W)
286 self.buttons.columnconfigure(2, weight=1)
288 def newevent(self, event):
289 self.event.displayunref(self.canvas)
292 self.event.displayref(self.canvas)
296 EventView(self.event, self.canvas)
299 prev = self.event.prev()
302 while (prev.real == 0):
309 next = self.event.next()
312 while (next.real == 0):
318 def linkpress(self, wevent):
319 event = self.event.getlinked()
326 def __init__(self, source, cpu, timestamp, last=0):
329 self.timestamp = int(timestamp)
338 source.lastevent(self)
343 statstr = self.name + " " + self.source.name
344 statstr += " on: cpu" + str(self.cpu)
345 statstr += " at: " + str(self.timestamp)
346 statstr += self.stattxt()
352 def textadd(self, tuple):
354 self.entries.append(tuple)
357 return [("Source:", self.source.name, 0),
358 ("Event:", self.name, 0),
359 ("CPU:", self.cpu, 0),
360 ("Timestamp:", self.timestamp, 0)] + self.entries
361 def mouseenter(self, canvas, item):
362 self.displayref(canvas)
365 def mouseexit(self, canvas, item):
366 self.displayunref(canvas)
369 def mousepress(self, canvas, item):
370 EventView(self, canvas)
373 return self.source.eventat(self.idx + 1)
376 return self.source.eventat(self.idx - 1)
378 def displayref(self, canvas):
379 if (self.dispcnt == 0):
380 canvas.itemconfigure(self.item, width=2)
383 def displayunref(self, canvas):
385 if (self.dispcnt == 0):
386 canvas.itemconfigure(self.item, width=0)
387 canvas.tag_raise("point", "state")
390 return self.linked.findevent(self.timestamp)
392 class PointEvent(Event):
393 def __init__(self, thread, cpu, timestamp, last=0):
394 Event.__init__(self, thread, cpu, timestamp, last)
396 def draw(self, canvas, xpos, ypos):
397 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
398 fill=self.color, tags=("all", "point", "event")
399 + (self.name,), width=0)
400 canvas.events[l] = self
402 if (self.enabled == 0):
403 canvas.itemconfigure(l, state="hidden")
407 class StateEvent(Event):
408 def __init__(self, thread, cpu, timestamp, last=0):
409 Event.__init__(self, thread, cpu, timestamp, last)
415 def draw(self, canvas, xpos, ypos):
416 next = self.nextstate()
417 if (self.skipself == 1 or next == None):
419 while (self.skipnext):
423 next = next.nextstate()
427 self.duration = next.timestamp - self.timestamp
428 if (self.duration < 0):
430 print "Unsynchronized timestamp"
431 print self.cpu, self.timestamp
432 print next.cpu, next.timestamp
433 delta = self.duration / canvas.ratio
434 l = canvas.create_rectangle(xpos, ypos,
435 xpos + delta, ypos - 10, fill=self.color, width=0,
436 tags=("all", "state", "event") + (self.name,))
437 canvas.events[l] = self
439 if (self.enabled == 0):
440 canvas.itemconfigure(l, state="hidden")
442 return (xpos + delta)
445 return " duration: " + ticks2sec(self.duration)
449 while (next != None and next.state == 0):
454 return [("Source:", self.source.name, 0),
455 ("Event:", self.name, 0),
456 ("Timestamp:", self.timestamp, 0),
457 ("CPU:", self.cpu, 0),
458 ("Duration:", ticks2sec(self.duration), 0)] \
465 def __init__(self, source, cpu, timestamp, count):
466 self.count = int(count)
467 Event.__init__(self, source, cpu, timestamp)
469 self.textadd(("count:", self.count, 0))
471 def draw(self, canvas, xpos, ypos):
473 self.duration = next.timestamp - self.timestamp
474 delta = self.duration / canvas.ratio
475 yhight = self.source.yscale() * self.count
476 l = canvas.create_rectangle(xpos, ypos - yhight,
477 xpos + delta, ypos, fill=self.color, width=0,
478 tags=("all", "count", "event") + (self.name,))
479 canvas.events[l] = self
481 if (self.enabled == 0):
482 canvas.itemconfigure(l, state="hidden")
483 return (xpos + delta)
486 return " count: " + str(self.count)
488 configtypes.append(Count)
490 class Running(StateEvent):
494 def __init__(self, thread, cpu, timestamp, prio):
495 StateEvent.__init__(self, thread, cpu, timestamp)
497 self.textadd(("prio:", self.prio, 0))
499 configtypes.append(Running)
501 class Idle(StateEvent):
505 def __init__(self, thread, cpu, timestamp, prio):
506 StateEvent.__init__(self, thread, cpu, timestamp)
508 self.textadd(("prio:", self.prio, 0))
510 configtypes.append(Idle)
512 class Yielding(StateEvent):
516 def __init__(self, thread, cpu, timestamp, prio):
517 StateEvent.__init__(self, thread, cpu, timestamp)
520 self.textadd(("prio:", self.prio, 0))
522 configtypes.append(Yielding)
524 class Swapped(StateEvent):
528 def __init__(self, thread, cpu, timestamp, prio):
529 StateEvent.__init__(self, thread, cpu, timestamp)
531 self.textadd(("prio:", self.prio, 0))
533 configtypes.append(Swapped)
535 class Suspended(StateEvent):
539 def __init__(self, thread, cpu, timestamp, prio):
540 StateEvent.__init__(self, thread, cpu, timestamp)
542 self.textadd(("prio:", self.prio, 0))
544 configtypes.append(Suspended)
546 class Iwait(StateEvent):
550 def __init__(self, thread, cpu, timestamp, prio):
551 StateEvent.__init__(self, thread, cpu, timestamp)
553 self.textadd(("prio:", self.prio, 0))
555 configtypes.append(Iwait)
557 class Preempted(StateEvent):
561 def __init__(self, thread, cpu, timestamp, prio, bythread):
562 StateEvent.__init__(self, thread, cpu, timestamp)
565 self.linked = bythread
566 self.textadd(("prio:", self.prio, 0))
567 self.textadd(("by thread:", self.linked.name, 1))
569 configtypes.append(Preempted)
571 class Sleep(StateEvent):
575 def __init__(self, thread, cpu, timestamp, prio, wmesg):
576 StateEvent.__init__(self, thread, cpu, timestamp)
579 self.textadd(("prio:", self.prio, 0))
580 self.textadd(("wmesg:", self.wmesg, 0))
583 statstr = StateEvent.stattxt(self)
584 statstr += " sleeping on: " + self.wmesg
587 configtypes.append(Sleep)
589 class Blocked(StateEvent):
593 def __init__(self, thread, cpu, timestamp, prio, lock):
594 StateEvent.__init__(self, thread, cpu, timestamp)
597 self.textadd(("prio:", self.prio, 0))
598 self.textadd(("lock:", self.lock, 0))
601 statstr = StateEvent.stattxt(self)
602 statstr += " blocked on: " + self.lock
605 configtypes.append(Blocked)
607 class KsegrpRunq(StateEvent):
611 def __init__(self, thread, cpu, timestamp, prio, bythread):
612 StateEvent.__init__(self, thread, cpu, timestamp)
614 self.linked = bythread
615 self.textadd(("prio:", self.prio, 0))
616 self.textadd(("by thread:", self.linked.name, 1))
618 configtypes.append(KsegrpRunq)
620 class Runq(StateEvent):
624 def __init__(self, thread, cpu, timestamp, prio, bythread):
625 StateEvent.__init__(self, thread, cpu, timestamp)
627 self.linked = bythread
628 self.textadd(("prio:", self.prio, 0))
629 self.textadd(("by thread:", self.linked.name, 1))
631 configtypes.append(Runq)
633 class Sched_exit(StateEvent):
637 def __init__(self, thread, cpu, timestamp, prio):
638 StateEvent.__init__(self, thread, cpu, timestamp)
639 self.name = "sched_exit"
641 self.textadd(("prio:", self.prio, 0))
643 configtypes.append(Sched_exit)
645 class Padevent(StateEvent):
646 def __init__(self, thread, cpu, timestamp, last=0):
647 StateEvent.__init__(self, thread, cpu, timestamp, last)
651 def draw(self, canvas, xpos, ypos):
655 self.duration = next.timestamp - self.timestamp
656 delta = self.duration / canvas.ratio
657 return (xpos + delta)
659 class Tick(PointEvent):
663 def __init__(self, thread, cpu, timestamp, prio, stathz):
664 PointEvent.__init__(self, thread, cpu, timestamp)
666 self.textadd(("prio:", self.prio, 0))
668 configtypes.append(Tick)
670 class Prio(PointEvent):
674 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
675 PointEvent.__init__(self, thread, cpu, timestamp)
677 self.newprio = newprio
678 self.linked = bythread
679 self.textadd(("new prio:", self.newprio, 0))
680 self.textadd(("prio:", self.prio, 0))
681 if (self.linked != self.source):
682 self.textadd(("by thread:", self.linked.name, 1))
684 self.textadd(("by thread:", self.linked.name, 0))
686 configtypes.append(Prio)
688 class Lend(PointEvent):
692 def __init__(self, thread, cpu, timestamp, prio, tothread):
693 PointEvent.__init__(self, thread, cpu, timestamp)
695 self.linked = tothread
696 self.textadd(("prio:", self.prio, 0))
697 self.textadd(("to thread:", self.linked.name, 1))
699 configtypes.append(Lend)
701 class Wokeup(PointEvent):
705 def __init__(self, thread, cpu, timestamp, ranthread):
706 PointEvent.__init__(self, thread, cpu, timestamp)
707 self.linked = ranthread
708 self.textadd(("ran thread:", self.linked.name, 1))
710 configtypes.append(Wokeup)
713 def __init__(self, name):
722 def event(self, event):
723 self.events.insert(0, event)
725 def remove(self, event):
726 self.events.remove(event)
728 def lastevent(self, event):
729 self.events.append(event)
731 def draw(self, canvas, ypos):
734 self.cpu = self.events[1].cpu
735 for i in range(0, len(self.events)):
736 self.events[i].idx = i
737 for event in self.events:
738 if (event.cpu != self.cpu and event.cpu != -1):
739 self.drawcpu(canvas, xpos, ypos)
742 xpos = event.draw(canvas, xpos, ypos)
743 self.drawcpu(canvas, xpos, ypos)
745 def drawname(self, canvas, ypos):
746 ypos = ypos - (self.ysize() / 2)
747 canvas.create_text(10, ypos, anchor="w", text=self.name)
749 def drawcpu(self, canvas, xpos, ypos):
758 color = 'light green'
760 color = 'blanched almond'
764 color = 'light slate blue'
769 l = canvas.create_rectangle(self.cpux,
770 ypos - self.ysize() - canvas.bdheight,
771 xpos, ypos + canvas.bdheight, fill=color, width=0,
772 tags=("all", "cpuinfo"))
777 def eventat(self, i):
778 if (i >= len(self.events)):
780 event = self.events[i]
783 def findevent(self, timestamp):
784 for event in self.events:
785 if (event.timestamp >= timestamp and event.real):
789 class Thread(EventSource):
791 def __init__(self, td, pcomm):
792 EventSource.__init__(self, pcomm)
795 cnt = Thread.names[pcomm]
797 Thread.names[pcomm] = 0
799 Thread.names[pcomm] = cnt + 1
802 cnt = Thread.names[self.name]
806 Thread.names[self.name] = cnt
807 self.name += " td" + str(cnt)
812 class Counter(EventSource):
814 def __init__(self, name):
815 EventSource.__init__(self, name)
817 def event(self, event):
818 EventSource.event(self, event)
824 if (count > Counter.max):
831 return (self.ysize() / Counter.max)
835 def __init__(self, file):
836 self.timestamp_first = {}
837 self.timestamp_last = {}
838 self.timestamp_adjust = {}
839 self.timestamp_f = None
840 self.timestamp_l = None
852 print "first", self.timestamp_f, "last", self.timestamp_l
853 print "time span", self.timespan()
854 print "stathz", self.stathz
855 ticksps = self.ticksps()
856 print "Ticks per second", ticksps
858 def parse(self, file):
862 print "Can't open", file
865 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+"
866 tdname = "(\S+)\(([^)]*)\)"
867 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
869 ktrstr = "mi_switch: " + tdname
870 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
871 switchout_re = re.compile(ktrhdr + ktrstr)
873 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
874 idled_re = re.compile(ktrhdr + ktrstr)
876 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
878 preempted_re = re.compile(ktrhdr + ktrstr)
880 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
881 switchin_re = re.compile(ktrhdr + ktrstr)
883 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
884 sched_add_re = re.compile(ktrhdr + ktrstr)
886 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
887 setrunqueue_re = re.compile(ktrhdr + ktrstr)
889 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
890 sched_rem_re = re.compile(ktrhdr + ktrstr)
892 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
893 sched_exit_re = re.compile(ktrhdr + ktrstr)
895 ktrstr = "statclock: " + tdname + " prio (\d+)"
896 ktrstr += " stathz (\d+)"
897 sched_clock_re = re.compile(ktrhdr + ktrstr)
899 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
900 ktrstr += " newprio (\d+) by " + tdname
901 sched_prio_re = re.compile(ktrhdr + ktrstr)
903 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
904 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
906 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
907 critsec_re = re.compile(ktrhdr + ktrstr)
909 parsers = [[cpuload_re, self.cpuload],
910 [loadglobal_re, self.loadglobal],
911 [switchin_re, self.switchin],
912 [switchout_re, self.switchout],
913 [sched_add_re, self.sched_add],
914 [setrunqueue_re, self.sched_rem],
915 [sched_prio_re, self.sched_prio],
916 [preempted_re, self.preempted],
917 [sched_rem_re, self.sched_rem],
918 [sched_exit_re, self.sched_exit],
919 [sched_clock_re, self.sched_clock],
920 [critsec_re, self.critsec],
921 [idled_re, self.idled]]
923 lines = ifp.readlines()
924 self.synchstamp(lines)
927 if ((self.lineno % 1024) == 0):
928 status.startup("Parsing line " +
938 def synchstamp(self, lines):
939 status.startup("Rationalizing Timestamps")
940 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*")
942 m = tstamp_re.match(line)
944 self.addstamp(*m.groups())
946 self.monostamp(lines)
949 def monostamp(self, lines):
951 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*")
953 m = tstamp_re.match(line)
956 (cpu, timestamp) = m.groups()
957 timestamp = int(timestamp)
959 timestamp -= self.timestamp_adjust[cpu]
960 if (laststamp != None and timestamp > laststamp):
961 self.timestamp_adjust[cpu] += timestamp - laststamp
962 laststamp = timestamp
964 def addstamp(self, cpu, timestamp):
965 timestamp = int(timestamp)
968 if (timestamp > self.timestamp_first[cpu]):
971 self.timestamp_first[cpu] = timestamp
972 self.timestamp_last[cpu] = timestamp
975 base = self.timestamp_last[0]
976 for i in range(0, len(self.timestamp_last)):
977 if (self.timestamp_last[i] < base):
978 base = self.timestamp_last[i]
980 print "Adjusting to base stamp", base
981 for i in range(0, len(self.timestamp_last)):
982 self.timestamp_adjust[i] = self.timestamp_last[i] - base;
983 print "CPU ", i, "adjust by ", self.timestamp_adjust[i]
986 for i in range(0, len(self.timestamp_first)):
987 first = self.timestamp_first[i] - self.timestamp_adjust[i]
988 if (first > self.timestamp_f):
989 self.timestamp_f = first
992 for i in range(0, len(self.timestamp_last)):
993 last = self.timestamp_last[i] - self.timestamp_adjust[i]
994 if (last > self.timestamp_l):
995 self.timestamp_l = last
998 def checkstamp(self, cpu, timestamp):
1000 timestamp = int(timestamp)
1001 if (timestamp > self.timestamp_first[cpu]):
1002 print "Bad timestamp on line ", self.lineno
1004 timestamp -= self.timestamp_adjust[cpu]
1008 return (self.timestamp_f - self.timestamp_l);
1011 return (self.timespan() / self.ticks[0]) * int(self.stathz)
1013 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
1014 TDI_SUSPENDED = 0x0001
1015 TDI_SLEEPING = 0x0002
1016 TDI_SWAPPED = 0x0004
1020 timestamp = self.checkstamp(cpu, timestamp)
1021 if (timestamp == 0):
1023 inhibit = int(inhibit)
1024 thread = self.findtd(td, pcomm)
1025 if (inhibit & TDI_SWAPPED):
1026 Swapped(thread, cpu, timestamp, prio)
1027 elif (inhibit & TDI_SLEEPING):
1028 Sleep(thread, cpu, timestamp, prio, wmesg)
1029 elif (inhibit & TDI_LOCK):
1030 Blocked(thread, cpu, timestamp, prio, lock)
1031 elif (inhibit & TDI_IWAIT):
1032 Iwait(thread, cpu, timestamp, prio)
1033 elif (inhibit & TDI_SUSPENDED):
1034 Suspended(thread, cpu, timestamp, prio)
1035 elif (inhibit == 0):
1036 Yielding(thread, cpu, timestamp, prio)
1038 print "Unknown event", inhibit
1041 def idled(self, cpu, timestamp, td, pcomm, prio):
1042 timestamp = self.checkstamp(cpu, timestamp)
1043 if (timestamp == 0):
1045 thread = self.findtd(td, pcomm)
1046 Idle(thread, cpu, timestamp, prio)
1048 def preempted(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 Preempted(thread, cpu, timestamp, prio,
1054 self.findtd(bytd, bypcomm))
1056 def switchin(self, cpu, timestamp, td, pcomm, prio):
1057 timestamp = self.checkstamp(cpu, timestamp)
1058 if (timestamp == 0):
1060 thread = self.findtd(td, pcomm)
1061 Running(thread, cpu, timestamp, prio)
1063 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1064 timestamp = self.checkstamp(cpu, timestamp)
1065 if (timestamp == 0):
1067 thread = self.findtd(td, pcomm)
1068 bythread = self.findtd(bytd, bypcomm)
1069 Runq(thread, cpu, timestamp, prio, bythread)
1070 Wokeup(bythread, cpu, timestamp, thread)
1072 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1073 timestamp = self.checkstamp(cpu, timestamp)
1074 if (timestamp == 0):
1076 thread = self.findtd(td, pcomm)
1077 KsegrpRunq(thread, cpu, timestamp, prio,
1078 self.findtd(bytd, bypcomm))
1080 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1081 timestamp = self.checkstamp(cpu, timestamp)
1082 if (timestamp == 0):
1084 thread = self.findtd(td, pcomm)
1085 Sched_exit(thread, cpu, timestamp, prio)
1087 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1088 timestamp = self.checkstamp(cpu, timestamp)
1089 if (timestamp == 0):
1091 self.stathz = stathz
1094 ticks = self.ticks[cpu]
1097 self.ticks[cpu] += 1
1098 thread = self.findtd(td, pcomm)
1099 Tick(thread, cpu, timestamp, prio, stathz)
1101 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1102 if (prio == newprio):
1104 timestamp = self.checkstamp(cpu, timestamp)
1105 if (timestamp == 0):
1107 thread = self.findtd(td, pcomm)
1108 bythread = self.findtd(bytd, bypcomm)
1109 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1110 Lend(bythread, cpu, timestamp, newprio, thread)
1112 def cpuload(self, cpu, timestamp, count):
1113 timestamp = self.checkstamp(cpu, timestamp)
1114 if (timestamp == 0):
1118 load = self.load[cpu]
1120 load = Counter("cpu" + str(cpu) + " load")
1121 self.load[cpu] = load
1122 self.sources.insert(0, load)
1123 Count(load, cpu, timestamp, count)
1125 def loadglobal(self, cpu, timestamp, count):
1126 timestamp = self.checkstamp(cpu, timestamp)
1127 if (timestamp == 0):
1131 load = self.load[cpu]
1133 load = Counter("CPU load")
1134 self.load[cpu] = load
1135 self.sources.insert(0, load)
1136 Count(load, cpu, timestamp, count)
1138 def critsec(self, cpu, timestamp, td, pcomm, to):
1139 timestamp = self.checkstamp(cpu, timestamp)
1140 if (timestamp == 0):
1144 crit = self.crit[cpu]
1146 crit = Counter("Critical Section")
1147 self.crit[cpu] = crit
1148 self.sources.insert(0, crit)
1149 Count(crit, cpu, timestamp, to)
1151 def findtd(self, td, pcomm):
1152 for thread in self.threads:
1153 if (thread.str == td and thread.name == pcomm):
1155 thread = Thread(td, pcomm)
1156 self.threads.append(thread)
1157 self.sources.append(thread)
1161 for source in self.sources:
1162 Padevent(source, -1, self.timestamp_l)
1163 Padevent(source, -1, self.timestamp_f, last=1)
1166 class SchedDisplay(Canvas):
1167 def __init__(self, master):
1174 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1175 scrollregion=(0, 0, 800, 500))
1177 def setfile(self, ktrfile):
1178 self.ktrfile = ktrfile
1179 self.sources = ktrfile.sources
1183 xsize = self.xsize()
1184 for source in self.sources:
1185 status.startup("Drawing " + source.name)
1186 self.create_line(0, ypos, xsize, ypos,
1187 width=1, fill="black", tags=("all",))
1188 ypos += self.bdheight
1189 ypos += source.ysize()
1190 source.draw(self, ypos)
1191 ypos += self.bdheight
1193 self.tag_raise("point", "state")
1194 self.tag_lower("cpuinfo", "all")
1197 self.create_line(0, ypos, xsize, ypos,
1198 width=1, fill="black", tags=("all",))
1199 self.tag_bind("event", "<Enter>", self.mouseenter)
1200 self.tag_bind("event", "<Leave>", self.mouseexit)
1201 self.tag_bind("event", "<Button-1>", self.mousepress)
1203 def mouseenter(self, event):
1204 item, = self.find_withtag(CURRENT)
1205 event = self.events[item]
1206 event.mouseenter(self, item)
1208 def mouseexit(self, event):
1209 item, = self.find_withtag(CURRENT)
1210 event = self.events[item]
1211 event.mouseexit(self, item)
1213 def mousepress(self, event):
1214 item, = self.find_withtag(CURRENT)
1215 event = self.events[item]
1216 event.mousepress(self, item)
1218 def drawnames(self, canvas):
1219 status.startup("Drawing names")
1221 canvas.configure(scrollregion=(0, 0,
1222 canvas["width"], self.ysize()))
1223 for source in self.sources:
1224 canvas.create_line(0, ypos, canvas["width"], ypos,
1225 width=1, fill="black", tags=("all",))
1226 ypos += self.bdheight
1227 ypos += source.ysize()
1228 source.drawname(canvas, ypos)
1229 ypos += self.bdheight
1230 canvas.create_line(0, ypos, canvas["width"], ypos,
1231 width=1, fill="black", tags=("all",))
1234 return ((self.ktrfile.timespan() / self.ratio) + 20)
1238 for source in self.sources:
1239 ysize += source.ysize() + (self.bdheight * 2)
1242 def scaleset(self, ratio):
1243 if (self.ktrfile == None):
1245 oldratio = self.ratio
1246 xstart, ystart = self.xview()
1247 length = (float(self["width"]) / self.xsize())
1248 middle = xstart + (length / 2)
1251 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1252 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1254 length = (float(self["width"]) / self.xsize())
1255 xstart = middle - (length / 2)
1256 self.xview_moveto(xstart)
1261 def setcolor(self, tag, color):
1262 self.itemconfigure(tag, state="normal", fill=color)
1264 def hide(self, tag):
1265 self.itemconfigure(tag, state="hidden")
1267 class GraphMenu(Frame):
1268 def __init__(self, master):
1269 Frame.__init__(self, master, bd=2, relief=RAISED)
1270 self.view = Menubutton(self, text="Configure")
1271 self.viewmenu = Menu(self.view, tearoff=0)
1272 self.viewmenu.add_command(label="Events",
1274 self.view["menu"] = self.viewmenu
1275 self.view.pack(side=LEFT)
1281 class SchedGraph(Frame):
1282 def __init__(self, master):
1283 Frame.__init__(self, master)
1289 self.pack(expand=1, fill="both")
1292 self.draw(sys.argv[1])
1294 def buildwidgets(self):
1296 self.menu = GraphMenu(self)
1297 self.display = SchedDisplay(self)
1298 self.names = Canvas(self,
1299 width=100, height=self.display["height"],
1300 bg='grey', scrollregion=(0, 0, 50, 100))
1301 self.scale = Scaler(self, self.display)
1302 status = self.status = Status(self)
1303 self.scrollY = Scrollbar(self, orient="vertical",
1304 command=self.display_yview)
1305 self.display.scrollX = Scrollbar(self, orient="horizontal",
1306 command=self.display.xview)
1307 self.display["xscrollcommand"] = self.display.scrollX.set
1308 self.display["yscrollcommand"] = self.scrollY.set
1309 self.names["yscrollcommand"] = self.scrollY.set
1312 self.columnconfigure(1, weight=1)
1313 self.rowconfigure(1, weight=1)
1314 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1315 self.names.grid(row=1, column=0, sticky=N+S)
1316 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1317 self.scrollY.grid(row=1, column=2, sticky=N+S)
1318 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1320 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1321 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1323 def draw(self, file):
1324 self.master.update()
1325 ktrfile = KTRFile(file)
1326 self.display.setfile(ktrfile)
1327 self.display.drawnames(self.names)
1329 self.scale.set(250000)
1330 self.display.xview_moveto(0)
1332 def display_yview(self, *args):
1333 self.names.yview(*args)
1334 self.display.yview(*args)
1336 def setcolor(self, tag, color):
1337 self.display.setcolor(tag, color)
1339 def hide(self, tag):
1340 self.display.hide(tag)
1342 if (len(sys.argv) != 2):
1343 print "usage:", sys.argv[0], "<ktr file>"
1347 root.title("Scheduler Graph")
1348 graph = SchedGraph(root)