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.
39 # - Dump the trace to a file: 'ktrdump -ct > ktr.out'
40 # - Run the python script: 'python schedgraph.py ktr.out'
43 # 1) Add a per-thread summary display
44 # 2) Add bounding box style zoom.
46 # 4) Implement some sorting mechanism.
53 us = ticksps / 1000000
56 return (str(ticks) + "us")
59 return (str(ticks) + "ms")
61 return (str(ticks) + "s")
64 def __init__(self, master, target):
65 Frame.__init__(self, master)
66 self.scale = Scale(self, command=self.scaleset,
67 from_=1000, to_=10000000, orient=HORIZONTAL,
69 self.label = Label(self, text="Ticks per pixel")
70 self.label.pack(side=LEFT)
71 self.scale.pack(fill="both", expand=1)
73 self.scale.set(target.scaleget())
76 def scaleset(self, value):
77 self.target.scaleset(int(value))
83 def __init__(self, master):
84 Frame.__init__(self, master)
85 self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
86 self.label.pack(fill="both", expand=1)
90 self.label.config(text=str)
93 self.label.config(text="")
95 def startup(self, str):
99 class EventConf(Frame):
100 def __init__(self, master, name, color, enabled):
101 Frame.__init__(self, master)
103 self.color = StringVar()
104 self.color_default = color
105 self.color_current = color
106 self.color.set(color)
107 self.enabled = IntVar()
108 self.enabled_default = enabled
109 self.enabled_current = enabled
110 self.enabled.set(enabled)
114 self.label = Label(self, text=self.name, anchor=W)
115 self.sample = Canvas(self, width=24, height=24,
117 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
118 fill=self.color.get())
119 self.list = OptionMenu(self, self.color,
120 "dark red", "red", "pink",
121 "dark orange", "orange",
122 "yellow", "light yellow",
123 "dark green", "green", "light green",
124 "dark blue", "blue", "light blue",
125 "dark violet", "violet", "purple",
126 "dark grey", "light grey",
128 command=self.setcolor)
129 self.checkbox = Checkbutton(self, text="enabled",
130 variable=self.enabled)
131 self.label.grid(row=0, column=0, sticky=E+W)
132 self.sample.grid(row=0, column=1)
133 self.list.grid(row=0, column=2, sticky=E+W)
134 self.checkbox.grid(row=0, column=3)
135 self.columnconfigure(0, weight=1)
136 self.columnconfigure(2, minsize=110)
138 def setcolor(self, color):
139 self.color.set(color)
140 self.sample.itemconfigure(self.rect, fill=color)
145 if (self.color_current != self.color.get()):
147 if (self.enabled_current != self.enabled.get()):
149 self.color_current = self.color.get()
150 self.enabled_current = self.enabled.get()
152 if (self.enabled_current):
153 graph.setcolor(self.name, self.color_current)
155 graph.hide(self.name)
158 graph.setcolor(self.name, self.color_current)
161 self.setcolor(self.color_current)
162 self.enabled.set(self.enabled_current)
165 self.setcolor(self.color_default)
166 self.enabled.set(self.enabled_default)
168 class EventConfigure(Toplevel):
170 Toplevel.__init__(self)
172 self.title("Event Configuration")
173 self.items = LabelFrame(self, text="Event Type")
174 self.buttons = Frame(self)
176 self.items.grid(row=0, column=0, sticky=E+W)
177 self.columnconfigure(0, weight=1)
178 self.buttons.grid(row=1, column=0, sticky=E+W)
181 for type in configtypes:
182 self.additem(type.name, type.color, type.enabled)
184 def additem(self, name, color, enabled=1):
185 item = EventConf(self.items, name, color, enabled)
186 self.types.append(item)
187 item.grid(row=self.irow, column=0, sticky=E+W)
190 def drawbuttons(self):
191 self.apply = Button(self.buttons, text="Apply",
193 self.revert = Button(self.buttons, text="Revert",
195 self.default = Button(self.buttons, text="Default",
197 self.apply.grid(row=0, column=0, sticky=E+W)
198 self.revert.grid(row=0, column=1, sticky=E+W)
199 self.default.grid(row=0, column=2, sticky=E+W)
200 self.buttons.columnconfigure(0, weight=1)
201 self.buttons.columnconfigure(1, weight=1)
202 self.buttons.columnconfigure(2, weight=1)
205 for item in self.types:
209 for item in self.types:
213 for item in self.types:
216 class EventView(Toplevel):
217 def __init__(self, event, canvas):
218 Toplevel.__init__(self)
222 self.frame = Frame(self)
223 self.frame.grid(row=0, column=0, sticky=N+S+E+W)
224 self.buttons = Frame(self)
225 self.buttons.grid(row=1, column=0, sticky=E+W)
229 event.displayref(canvas)
230 self.bind("<Destroy>", self.destroycb)
232 def destroycb(self, event):
233 self.unbind("<Destroy>")
234 if (self.event != None):
235 self.event.displayunref(self.canvas)
239 def clearlabels(self):
240 for label in self.frame.grid_slaves():
243 def drawlabels(self):
245 labels = self.event.labels()
246 while (len(labels) < 7):
247 labels.append(("", "", 0))
249 name, value, linked = label
250 l = Label(self.frame, text=name, bd=1, width=15,
251 relief=SUNKEN, anchor=W)
256 r = Label(self.frame, text=value, bd=1,
257 relief=SUNKEN, anchor=W, fg=fgcolor)
258 l.grid(row=ypos, column=0, sticky=E+W)
259 r.grid(row=ypos, column=1, sticky=E+W)
261 r.bind("<Button-1>", self.linkpress)
263 self.frame.columnconfigure(1, minsize=80)
265 def drawbuttons(self):
266 self.back = Button(self.buttons, text="<", command=self.bpress)
267 self.forw = Button(self.buttons, text=">", command=self.fpress)
268 self.new = Button(self.buttons, text="new", command=self.npress)
269 self.back.grid(row=0, column=0, sticky=E+W)
270 self.forw.grid(row=0, column=1, sticky=E+W)
271 self.new.grid(row=0, column=2, sticky=E+W)
272 self.buttons.columnconfigure(2, weight=1)
274 def newevent(self, event):
275 self.event.displayunref(self.canvas)
278 self.event.displayref(self.canvas)
282 EventView(self.event, self.canvas)
285 prev = self.event.prev()
288 while (prev.real == 0):
295 next = self.event.next()
298 while (next.real == 0):
304 def linkpress(self, wevent):
305 event = self.event.getlinked()
312 def __init__(self, source, cpu, timestamp, last=0):
315 self.timestamp = int(timestamp)
324 source.lastevent(self)
329 statstr = self.name + " " + self.source.name
330 statstr += " on: cpu" + str(self.cpu)
331 statstr += " at: " + str(self.timestamp)
332 statstr += self.stattxt()
338 def textadd(self, tuple):
340 self.entries.append(tuple)
343 return [("Source:", self.source.name, 0),
344 ("Event:", self.name, 0),
345 ("CPU:", self.cpu, 0),
346 ("Timestamp:", self.timestamp, 0)] + self.entries
347 def mouseenter(self, canvas, item):
348 self.displayref(canvas)
351 def mouseexit(self, canvas, item):
352 self.displayunref(canvas)
355 def mousepress(self, canvas, item):
356 EventView(self, canvas)
359 return self.source.eventat(self.idx + 1)
362 return self.source.eventat(self.idx - 1)
364 def displayref(self, canvas):
365 if (self.dispcnt == 0):
366 canvas.itemconfigure(self.item, width=2)
369 def displayunref(self, canvas):
371 if (self.dispcnt == 0):
372 canvas.itemconfigure(self.item, width=0)
373 canvas.tag_raise("point", "state")
376 return self.linked.findevent(self.timestamp)
378 class PointEvent(Event):
379 def __init__(self, thread, cpu, timestamp, last=0):
380 Event.__init__(self, thread, cpu, timestamp, last)
382 def draw(self, canvas, xpos, ypos):
383 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
384 fill=self.color, tags=("all", "point", "event")
385 + (self.name,), width=0)
386 canvas.events[l] = self
388 if (self.enabled == 0):
389 canvas.itemconfigure(l, state="hidden")
393 class StateEvent(Event):
394 def __init__(self, thread, cpu, timestamp, last=0):
395 Event.__init__(self, thread, cpu, timestamp, last)
401 def draw(self, canvas, xpos, ypos):
402 next = self.nextstate()
403 if (self.skipself == 1 or next == None):
405 while (self.skipnext):
409 next = next.nextstate()
413 self.duration = next.timestamp - self.timestamp
414 if (self.duration < 0):
416 print "Unsynchronized timestamp"
417 print self.cpu, self.timestamp
418 print next.cpu, next.timestamp
419 delta = self.duration / canvas.ratio
420 l = canvas.create_rectangle(xpos, ypos,
421 xpos + delta, ypos - 10, fill=self.color, width=0,
422 tags=("all", "state", "event") + (self.name,))
423 canvas.events[l] = self
425 if (self.enabled == 0):
426 canvas.itemconfigure(l, state="hidden")
428 return (xpos + delta)
431 return " duration: " + ticks2sec(self.duration)
435 while (next != None and next.state == 0):
440 return [("Source:", self.source.name, 0),
441 ("Event:", self.name, 0),
442 ("Timestamp:", self.timestamp, 0),
443 ("CPU:", self.cpu, 0),
444 ("Duration:", ticks2sec(self.duration), 0)] \
451 def __init__(self, source, cpu, timestamp, count):
452 self.count = int(count)
453 Event.__init__(self, source, cpu, timestamp)
455 self.textadd(("count:", self.count, 0))
457 def draw(self, canvas, xpos, ypos):
459 self.duration = next.timestamp - self.timestamp
460 delta = self.duration / canvas.ratio
461 yhight = self.source.yscale() * self.count
462 l = canvas.create_rectangle(xpos, ypos - yhight,
463 xpos + delta, ypos, fill=self.color, width=0,
464 tags=("all", "count", "event") + (self.name,))
465 canvas.events[l] = self
467 if (self.enabled == 0):
468 canvas.itemconfigure(l, state="hidden")
469 return (xpos + delta)
472 return " count: " + str(self.count)
474 configtypes.append(Count)
476 class Running(StateEvent):
480 def __init__(self, thread, cpu, timestamp, prio):
481 StateEvent.__init__(self, thread, cpu, timestamp)
483 self.textadd(("prio:", self.prio, 0))
485 configtypes.append(Running)
487 class Idle(StateEvent):
491 def __init__(self, thread, cpu, timestamp, prio):
492 StateEvent.__init__(self, thread, cpu, timestamp)
494 self.textadd(("prio:", self.prio, 0))
496 configtypes.append(Idle)
498 class Yielding(StateEvent):
502 def __init__(self, thread, cpu, timestamp, prio):
503 StateEvent.__init__(self, thread, cpu, timestamp)
506 self.textadd(("prio:", self.prio, 0))
508 configtypes.append(Yielding)
510 class Swapped(StateEvent):
514 def __init__(self, thread, cpu, timestamp, prio):
515 StateEvent.__init__(self, thread, cpu, timestamp)
517 self.textadd(("prio:", self.prio, 0))
519 configtypes.append(Swapped)
521 class Suspended(StateEvent):
525 def __init__(self, thread, cpu, timestamp, prio):
526 StateEvent.__init__(self, thread, cpu, timestamp)
528 self.textadd(("prio:", self.prio, 0))
530 configtypes.append(Suspended)
532 class Iwait(StateEvent):
536 def __init__(self, thread, cpu, timestamp, prio):
537 StateEvent.__init__(self, thread, cpu, timestamp)
539 self.textadd(("prio:", self.prio, 0))
541 configtypes.append(Iwait)
543 class Preempted(StateEvent):
547 def __init__(self, thread, cpu, timestamp, prio, bythread):
548 StateEvent.__init__(self, thread, cpu, timestamp)
551 self.linked = bythread
552 self.textadd(("prio:", self.prio, 0))
553 self.textadd(("by thread:", self.linked.name, 1))
555 configtypes.append(Preempted)
557 class Sleep(StateEvent):
561 def __init__(self, thread, cpu, timestamp, prio, wmesg):
562 StateEvent.__init__(self, thread, cpu, timestamp)
565 self.textadd(("prio:", self.prio, 0))
566 self.textadd(("wmesg:", self.wmesg, 0))
569 statstr = StateEvent.stattxt(self)
570 statstr += " sleeping on: " + self.wmesg
573 configtypes.append(Sleep)
575 class Blocked(StateEvent):
579 def __init__(self, thread, cpu, timestamp, prio, lock):
580 StateEvent.__init__(self, thread, cpu, timestamp)
583 self.textadd(("prio:", self.prio, 0))
584 self.textadd(("lock:", self.lock, 0))
587 statstr = StateEvent.stattxt(self)
588 statstr += " blocked on: " + self.lock
591 configtypes.append(Blocked)
593 class KsegrpRunq(StateEvent):
597 def __init__(self, thread, cpu, timestamp, prio, bythread):
598 StateEvent.__init__(self, thread, cpu, timestamp)
600 self.linked = bythread
601 self.textadd(("prio:", self.prio, 0))
602 self.textadd(("by thread:", self.linked.name, 1))
604 configtypes.append(KsegrpRunq)
606 class Runq(StateEvent):
610 def __init__(self, thread, cpu, timestamp, prio, bythread):
611 StateEvent.__init__(self, thread, cpu, timestamp)
613 self.linked = bythread
614 self.textadd(("prio:", self.prio, 0))
615 self.textadd(("by thread:", self.linked.name, 1))
617 configtypes.append(Runq)
619 class Sched_exit(StateEvent):
623 def __init__(self, thread, cpu, timestamp, prio):
624 StateEvent.__init__(self, thread, cpu, timestamp)
625 self.name = "sched_exit"
627 self.textadd(("prio:", self.prio, 0))
629 configtypes.append(Sched_exit)
631 class Padevent(StateEvent):
632 def __init__(self, thread, cpu, timestamp, last=0):
633 StateEvent.__init__(self, thread, cpu, timestamp, last)
637 def draw(self, canvas, xpos, ypos):
641 self.duration = next.timestamp - self.timestamp
642 delta = self.duration / canvas.ratio
643 return (xpos + delta)
645 class Tick(PointEvent):
649 def __init__(self, thread, cpu, timestamp, prio, stathz):
650 PointEvent.__init__(self, thread, cpu, timestamp)
652 self.textadd(("prio:", self.prio, 0))
654 configtypes.append(Tick)
656 class Prio(PointEvent):
660 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
661 PointEvent.__init__(self, thread, cpu, timestamp)
663 self.newprio = newprio
664 self.linked = bythread
665 self.textadd(("new prio:", self.newprio, 0))
666 self.textadd(("prio:", self.prio, 0))
667 if (self.linked != self.source):
668 self.textadd(("by thread:", self.linked.name, 1))
670 self.textadd(("by thread:", self.linked.name, 0))
672 configtypes.append(Prio)
674 class Lend(PointEvent):
678 def __init__(self, thread, cpu, timestamp, prio, tothread):
679 PointEvent.__init__(self, thread, cpu, timestamp)
681 self.linked = tothread
682 self.textadd(("prio:", self.prio, 0))
683 self.textadd(("to thread:", self.linked.name, 1))
685 configtypes.append(Lend)
687 class Wokeup(PointEvent):
691 def __init__(self, thread, cpu, timestamp, ranthread):
692 PointEvent.__init__(self, thread, cpu, timestamp)
693 self.linked = ranthread
694 self.textadd(("ran thread:", self.linked.name, 1))
696 configtypes.append(Wokeup)
699 def __init__(self, name):
708 def event(self, event):
709 self.events.insert(0, event)
711 def remove(self, event):
712 self.events.remove(event)
714 def lastevent(self, event):
715 self.events.append(event)
717 def draw(self, canvas, ypos):
720 self.cpu = self.events[1].cpu
721 for i in range(0, len(self.events)):
722 self.events[i].idx = i
723 for event in self.events:
724 if (event.cpu != self.cpu and event.cpu != -1):
725 self.drawcpu(canvas, xpos, ypos)
728 xpos = event.draw(canvas, xpos, ypos)
729 self.drawcpu(canvas, xpos, ypos)
731 def drawname(self, canvas, ypos):
732 ypos = ypos - (self.ysize() / 2)
733 canvas.create_text(10, ypos, anchor="w", text=self.name)
735 def drawcpu(self, canvas, xpos, ypos):
744 color = 'light green'
746 color = 'blanched almond'
750 color = 'light slate blue'
755 l = canvas.create_rectangle(self.cpux,
756 ypos - self.ysize() - canvas.bdheight,
757 xpos, ypos + canvas.bdheight, fill=color, width=0,
758 tags=("all", "cpuinfo"))
763 def eventat(self, i):
764 if (i >= len(self.events)):
766 event = self.events[i]
769 def findevent(self, timestamp):
770 for event in self.events:
771 if (event.timestamp >= timestamp and event.real):
775 class Thread(EventSource):
777 def __init__(self, td, pcomm):
778 EventSource.__init__(self, pcomm)
781 cnt = Thread.names[pcomm]
783 Thread.names[pcomm] = 0
785 Thread.names[pcomm] = cnt + 1
788 cnt = Thread.names[self.name]
792 Thread.names[self.name] = cnt
793 self.name += " td" + str(cnt)
798 class Counter(EventSource):
800 def __init__(self, name):
801 EventSource.__init__(self, name)
803 def event(self, event):
804 EventSource.event(self, event)
810 if (count > Counter.max):
817 return (self.ysize() / Counter.max)
821 def __init__(self, file):
822 self.timestamp_first = {}
823 self.timestamp_last = {}
824 self.timestamp_adjust = {}
825 self.timestamp_f = None
826 self.timestamp_l = None
838 print "first", self.timestamp_f, "last", self.timestamp_l
839 print "time span", self.timespan()
840 print "stathz", self.stathz
841 ticksps = self.ticksps()
842 print "Ticks per second", ticksps
844 def parse(self, file):
848 print "Can't open", file
851 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+"
852 tdname = "(\S+)\(([^)]*)\)"
853 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
855 ktrstr = "mi_switch: " + tdname
856 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
857 switchout_re = re.compile(ktrhdr + ktrstr)
859 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
860 idled_re = re.compile(ktrhdr + ktrstr)
862 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
864 preempted_re = re.compile(ktrhdr + ktrstr)
866 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
867 switchin_re = re.compile(ktrhdr + ktrstr)
869 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
870 sched_add_re = re.compile(ktrhdr + ktrstr)
872 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
873 setrunqueue_re = re.compile(ktrhdr + ktrstr)
875 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
876 sched_rem_re = re.compile(ktrhdr + ktrstr)
878 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
879 sched_exit_re = re.compile(ktrhdr + ktrstr)
881 ktrstr = "statclock: " + tdname + " prio (\d+)"
882 ktrstr += " stathz (\d+)"
883 sched_clock_re = re.compile(ktrhdr + ktrstr)
885 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
886 ktrstr += " newprio (\d+) by " + tdname
887 sched_prio_re = re.compile(ktrhdr + ktrstr)
889 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
890 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
892 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
893 critsec_re = re.compile(ktrhdr + ktrstr)
895 parsers = [[cpuload_re, self.cpuload],
896 [loadglobal_re, self.loadglobal],
897 [switchin_re, self.switchin],
898 [switchout_re, self.switchout],
899 [sched_add_re, self.sched_add],
900 [setrunqueue_re, self.sched_rem],
901 [sched_prio_re, self.sched_prio],
902 [preempted_re, self.preempted],
903 [sched_rem_re, self.sched_rem],
904 [sched_exit_re, self.sched_exit],
905 [sched_clock_re, self.sched_clock],
906 [critsec_re, self.critsec],
907 [idled_re, self.idled]]
909 lines = ifp.readlines()
910 self.synchstamp(lines)
913 if ((self.lineno % 1024) == 0):
914 status.startup("Parsing line " +
924 def synchstamp(self, lines):
925 status.startup("Rationalizing Timestamps")
926 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*")
928 m = tstamp_re.match(line)
930 self.addstamp(*m.groups())
932 self.monostamp(lines)
935 def monostamp(self, lines):
937 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*")
939 m = tstamp_re.match(line)
942 (cpu, timestamp) = m.groups()
943 timestamp = int(timestamp)
945 timestamp -= self.timestamp_adjust[cpu]
946 if (laststamp != None and timestamp > laststamp):
947 self.timestamp_adjust[cpu] += timestamp - laststamp
948 laststamp = timestamp
950 def addstamp(self, cpu, timestamp):
951 timestamp = int(timestamp)
954 if (timestamp > self.timestamp_first[cpu]):
957 self.timestamp_first[cpu] = timestamp
958 self.timestamp_last[cpu] = timestamp
961 base = self.timestamp_last[0]
962 for i in range(0, len(self.timestamp_last)):
963 if (self.timestamp_last[i] < base):
964 base = self.timestamp_last[i]
966 print "Adjusting to base stamp", base
967 for i in range(0, len(self.timestamp_last)):
968 self.timestamp_adjust[i] = self.timestamp_last[i] - base;
969 print "CPU ", i, "adjust by ", self.timestamp_adjust[i]
972 for i in range(0, len(self.timestamp_first)):
973 first = self.timestamp_first[i] - self.timestamp_adjust[i]
974 if (first > self.timestamp_f):
975 self.timestamp_f = first
978 for i in range(0, len(self.timestamp_last)):
979 last = self.timestamp_last[i] - self.timestamp_adjust[i]
980 if (last > self.timestamp_l):
981 self.timestamp_l = last
984 def checkstamp(self, cpu, timestamp):
986 timestamp = int(timestamp)
987 if (timestamp > self.timestamp_first[cpu]):
988 print "Bad timestamp on line ", self.lineno
990 timestamp -= self.timestamp_adjust[cpu]
994 return (self.timestamp_f - self.timestamp_l);
997 return (self.timespan() / self.ticks[0]) * int(self.stathz)
999 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
1000 TDI_SUSPENDED = 0x0001
1001 TDI_SLEEPING = 0x0002
1002 TDI_SWAPPED = 0x0004
1006 timestamp = self.checkstamp(cpu, timestamp)
1007 if (timestamp == 0):
1009 inhibit = int(inhibit)
1010 thread = self.findtd(td, pcomm)
1011 if (inhibit & TDI_SWAPPED):
1012 Swapped(thread, cpu, timestamp, prio)
1013 elif (inhibit & TDI_SLEEPING):
1014 Sleep(thread, cpu, timestamp, prio, wmesg)
1015 elif (inhibit & TDI_LOCK):
1016 Blocked(thread, cpu, timestamp, prio, lock)
1017 elif (inhibit & TDI_IWAIT):
1018 Iwait(thread, cpu, timestamp, prio)
1019 elif (inhibit & TDI_SUSPENDED):
1020 Suspended(thread, cpu, timestamp, prio)
1021 elif (inhibit == 0):
1022 Yielding(thread, cpu, timestamp, prio)
1024 print "Unknown event", inhibit
1027 def idled(self, cpu, timestamp, td, pcomm, prio):
1028 timestamp = self.checkstamp(cpu, timestamp)
1029 if (timestamp == 0):
1031 thread = self.findtd(td, pcomm)
1032 Idle(thread, cpu, timestamp, prio)
1034 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1035 timestamp = self.checkstamp(cpu, timestamp)
1036 if (timestamp == 0):
1038 thread = self.findtd(td, pcomm)
1039 Preempted(thread, cpu, timestamp, prio,
1040 self.findtd(bytd, bypcomm))
1042 def switchin(self, cpu, timestamp, td, pcomm, prio):
1043 timestamp = self.checkstamp(cpu, timestamp)
1044 if (timestamp == 0):
1046 thread = self.findtd(td, pcomm)
1047 Running(thread, cpu, timestamp, prio)
1049 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1050 timestamp = self.checkstamp(cpu, timestamp)
1051 if (timestamp == 0):
1053 thread = self.findtd(td, pcomm)
1054 bythread = self.findtd(bytd, bypcomm)
1055 Runq(thread, cpu, timestamp, prio, bythread)
1056 Wokeup(bythread, cpu, timestamp, thread)
1058 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1059 timestamp = self.checkstamp(cpu, timestamp)
1060 if (timestamp == 0):
1062 thread = self.findtd(td, pcomm)
1063 KsegrpRunq(thread, cpu, timestamp, prio,
1064 self.findtd(bytd, bypcomm))
1066 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1067 timestamp = self.checkstamp(cpu, timestamp)
1068 if (timestamp == 0):
1070 thread = self.findtd(td, pcomm)
1071 Sched_exit(thread, cpu, timestamp, prio)
1073 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1074 timestamp = self.checkstamp(cpu, timestamp)
1075 if (timestamp == 0):
1077 self.stathz = stathz
1080 ticks = self.ticks[cpu]
1083 self.ticks[cpu] += 1
1084 thread = self.findtd(td, pcomm)
1085 Tick(thread, cpu, timestamp, prio, stathz)
1087 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1088 if (prio == newprio):
1090 timestamp = self.checkstamp(cpu, timestamp)
1091 if (timestamp == 0):
1093 thread = self.findtd(td, pcomm)
1094 bythread = self.findtd(bytd, bypcomm)
1095 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1096 Lend(bythread, cpu, timestamp, newprio, thread)
1098 def cpuload(self, cpu, timestamp, count):
1099 timestamp = self.checkstamp(cpu, timestamp)
1100 if (timestamp == 0):
1104 load = self.load[cpu]
1106 load = Counter("cpu" + str(cpu) + " load")
1107 self.load[cpu] = load
1108 self.sources.insert(0, load)
1109 Count(load, cpu, timestamp, count)
1111 def loadglobal(self, cpu, timestamp, count):
1112 timestamp = self.checkstamp(cpu, timestamp)
1113 if (timestamp == 0):
1117 load = self.load[cpu]
1119 load = Counter("CPU load")
1120 self.load[cpu] = load
1121 self.sources.insert(0, load)
1122 Count(load, cpu, timestamp, count)
1124 def critsec(self, cpu, timestamp, td, pcomm, to):
1125 timestamp = self.checkstamp(cpu, timestamp)
1126 if (timestamp == 0):
1130 crit = self.crit[cpu]
1132 crit = Counter("Critical Section")
1133 self.crit[cpu] = crit
1134 self.sources.insert(0, crit)
1135 Count(crit, cpu, timestamp, to)
1137 def findtd(self, td, pcomm):
1138 for thread in self.threads:
1139 if (thread.str == td and thread.name == pcomm):
1141 thread = Thread(td, pcomm)
1142 self.threads.append(thread)
1143 self.sources.append(thread)
1147 for source in self.sources:
1148 Padevent(source, -1, self.timestamp_l)
1149 Padevent(source, -1, self.timestamp_f, last=1)
1152 class SchedDisplay(Canvas):
1153 def __init__(self, master):
1160 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1161 scrollregion=(0, 0, 800, 500))
1163 def setfile(self, ktrfile):
1164 self.ktrfile = ktrfile
1165 self.sources = ktrfile.sources
1169 xsize = self.xsize()
1170 for source in self.sources:
1171 status.startup("Drawing " + source.name)
1172 self.create_line(0, ypos, xsize, ypos,
1173 width=1, fill="black", tags=("all",))
1174 ypos += self.bdheight
1175 ypos += source.ysize()
1176 source.draw(self, ypos)
1177 ypos += self.bdheight
1179 self.tag_raise("point", "state")
1180 self.tag_lower("cpuinfo", "all")
1183 self.create_line(0, ypos, xsize, ypos,
1184 width=1, fill="black", tags=("all",))
1185 self.tag_bind("event", "<Enter>", self.mouseenter)
1186 self.tag_bind("event", "<Leave>", self.mouseexit)
1187 self.tag_bind("event", "<Button-1>", self.mousepress)
1189 def mouseenter(self, event):
1190 item, = self.find_withtag(CURRENT)
1191 event = self.events[item]
1192 event.mouseenter(self, item)
1194 def mouseexit(self, event):
1195 item, = self.find_withtag(CURRENT)
1196 event = self.events[item]
1197 event.mouseexit(self, item)
1199 def mousepress(self, event):
1200 item, = self.find_withtag(CURRENT)
1201 event = self.events[item]
1202 event.mousepress(self, item)
1204 def drawnames(self, canvas):
1205 status.startup("Drawing names")
1207 canvas.configure(scrollregion=(0, 0,
1208 canvas["width"], self.ysize()))
1209 for source in self.sources:
1210 canvas.create_line(0, ypos, canvas["width"], ypos,
1211 width=1, fill="black", tags=("all",))
1212 ypos += self.bdheight
1213 ypos += source.ysize()
1214 source.drawname(canvas, ypos)
1215 ypos += self.bdheight
1216 canvas.create_line(0, ypos, canvas["width"], ypos,
1217 width=1, fill="black", tags=("all",))
1220 return ((self.ktrfile.timespan() / self.ratio) + 20)
1224 for source in self.sources:
1225 ysize += source.ysize() + (self.bdheight * 2)
1228 def scaleset(self, ratio):
1229 if (self.ktrfile == None):
1231 oldratio = self.ratio
1232 xstart, ystart = self.xview()
1233 length = (float(self["width"]) / self.xsize())
1234 middle = xstart + (length / 2)
1237 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1238 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1240 length = (float(self["width"]) / self.xsize())
1241 xstart = middle - (length / 2)
1242 self.xview_moveto(xstart)
1247 def setcolor(self, tag, color):
1248 self.itemconfigure(tag, state="normal", fill=color)
1250 def hide(self, tag):
1251 self.itemconfigure(tag, state="hidden")
1253 class GraphMenu(Frame):
1254 def __init__(self, master):
1255 Frame.__init__(self, master, bd=2, relief=RAISED)
1256 self.view = Menubutton(self, text="Configure")
1257 self.viewmenu = Menu(self.view, tearoff=0)
1258 self.viewmenu.add_command(label="Events",
1260 self.view["menu"] = self.viewmenu
1261 self.view.pack(side=LEFT)
1267 class SchedGraph(Frame):
1268 def __init__(self, master):
1269 Frame.__init__(self, master)
1275 self.pack(expand=1, fill="both")
1278 self.draw(sys.argv[1])
1280 def buildwidgets(self):
1282 self.menu = GraphMenu(self)
1283 self.display = SchedDisplay(self)
1284 self.names = Canvas(self,
1285 width=100, height=self.display["height"],
1286 bg='grey', scrollregion=(0, 0, 50, 100))
1287 self.scale = Scaler(self, self.display)
1288 status = self.status = Status(self)
1289 self.scrollY = Scrollbar(self, orient="vertical",
1290 command=self.display_yview)
1291 self.display.scrollX = Scrollbar(self, orient="horizontal",
1292 command=self.display.xview)
1293 self.display["xscrollcommand"] = self.display.scrollX.set
1294 self.display["yscrollcommand"] = self.scrollY.set
1295 self.names["yscrollcommand"] = self.scrollY.set
1298 self.columnconfigure(1, weight=1)
1299 self.rowconfigure(1, weight=1)
1300 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1301 self.names.grid(row=1, column=0, sticky=N+S)
1302 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1303 self.scrollY.grid(row=1, column=2, sticky=N+S)
1304 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1306 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1307 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1309 def draw(self, file):
1310 self.master.update()
1311 ktrfile = KTRFile(file)
1312 self.display.setfile(ktrfile)
1313 self.display.drawnames(self.names)
1315 self.scale.set(250000)
1316 self.display.xview_moveto(0)
1318 def display_yview(self, *args):
1319 self.names.yview(*args)
1320 self.display.yview(*args)
1322 def setcolor(self, tag, color):
1323 self.display.setcolor(tag, color)
1325 def hide(self, tag):
1326 self.display.hide(tag)
1328 if (len(sys.argv) != 2):
1329 print "usage:", sys.argv[0], "<ktr file>"
1333 root.title("Scheduler Graph")
1334 graph = SchedGraph(root)