Deleted Added
full compact
schedgraph.py (143565) schedgraph.py (152131)
1#!/usr/local/bin/python
2
3# Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org>
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10# notice unmodified, this list of conditions, and the following
11# disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
1#!/usr/local/bin/python
2
3# Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org>
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10# notice unmodified, this list of conditions, and the following
11# disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# $FreeBSD: head/tools/sched/schedgraph.py 143565 2005-03-14 11:52:24Z jeff $
27# $FreeBSD: head/tools/sched/schedgraph.py 152131 2005-11-06 17:43:25Z rwatson $
28
29import sys
30import re
31from Tkinter import *
32
33# To use:
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'
41#
42# To do:
43# 1) Add a per-thread summary display
44# 2) Add bounding box style zoom.
45# 3) Click to center.
46# 4) Implement some sorting mechanism.
47
48ticksps = None
49status = None
50configtypes = []
51
52def ticks2sec(ticks):
53 ns = ticksps / 1000000000
54 ticks /= ns
55 if (ticks < 1000):
56 return (str(ticks) + "ns")
57 ticks /= 1000
58 if (ticks < 1000):
59 return (str(ticks) + "us")
60 ticks /= 1000
61 if (ticks < 1000):
62 return (str(ticks) + "ms")
63 ticks /= 1000
64 return (str(ticks) + "s")
65
66class Scaler(Frame):
67 def __init__(self, master, target):
68 Frame.__init__(self, master)
69 self.scale = Scale(self, command=self.scaleset,
70 from_=1000, to_=1000000, orient=HORIZONTAL, resolution=1000)
71 self.label = Label(self, text="Ticks per pixel")
72 self.label.pack(side=LEFT)
73 self.scale.pack(fill="both", expand=1)
74 self.target = target
75 self.scale.set(target.scaleget())
76 self.initialized = 1
77
78 def scaleset(self, value):
79 self.target.scaleset(int(value))
80
81 def set(self, value):
82 self.scale.set(value)
83
84class Status(Frame):
85 def __init__(self, master):
86 Frame.__init__(self, master)
87 self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
88 self.label.pack(fill="both", expand=1)
89 self.clear()
90
91 def set(self, str):
92 self.label.config(text=str)
93
94 def clear(self):
95 self.label.config(text="")
96
97 def startup(self, str):
98 self.set(str)
99 root.update()
100
101class EventConf(Frame):
102 def __init__(self, master, name, color, enabled):
103 Frame.__init__(self, master)
104 self.name = name
105 self.color = StringVar()
106 self.color_default = color
107 self.color_current = color
108 self.color.set(color)
109 self.enabled = IntVar()
110 self.enabled_default = enabled
111 self.enabled_current = enabled
112 self.enabled.set(enabled)
113 self.draw()
114
115 def draw(self):
116 self.label = Label(self, text=self.name, anchor=W)
117 self.sample = Canvas(self, width=24, height=24,
118 bg='grey')
119 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
120 fill=self.color.get())
121 self.list = OptionMenu(self, self.color,
122 "dark red", "red", "pink",
123 "dark orange", "orange",
124 "yellow", "light yellow",
125 "dark green", "green", "light green",
126 "dark blue", "blue", "light blue",
127 "dark violet", "violet", "purple",
128 "dark grey", "light grey",
129 "white", "black",
130 command=self.setcolor)
131 self.checkbox = Checkbutton(self, text="enabled",
132 variable=self.enabled)
133 self.label.grid(row=0, column=0, sticky=E+W)
134 self.sample.grid(row=0, column=1)
135 self.list.grid(row=0, column=2, sticky=E+W)
136 self.checkbox.grid(row=0, column=3)
137 self.columnconfigure(0, weight=1)
138 self.columnconfigure(2, minsize=110)
139
140 def setcolor(self, color):
141 self.color.set(color)
142 self.sample.itemconfigure(self.rect, fill=color)
143
144 def apply(self):
145 cchange = 0
146 echange = 0
147 if (self.color_current != self.color.get()):
148 cchange = 1
149 if (self.enabled_current != self.enabled.get()):
150 echange = 1
151 self.color_current = self.color.get()
152 self.enabled_current = self.enabled.get()
153 if (echange != 0):
154 if (self.enabled_current):
155 graph.setcolor(self.name, self.color_current)
156 else:
157 graph.hide(self.name)
158 return
159 if (cchange != 0):
160 graph.setcolor(self.name, self.color_current)
161
162 def revert(self):
163 self.setcolor(self.color_current)
164 self.enabled.set(self.enabled_current)
165
166 def default(self):
167 self.setcolor(self.color_default)
168 self.enabled.set(self.enabled_default)
169
170class EventConfigure(Toplevel):
171 def __init__(self):
172 Toplevel.__init__(self)
173 self.resizable(0, 0)
174 self.title("Event Configuration")
175 self.items = LabelFrame(self, text="Event Type")
176 self.buttons = Frame(self)
177 self.drawbuttons()
178 self.items.grid(row=0, column=0, sticky=E+W)
179 self.columnconfigure(0, weight=1)
180 self.buttons.grid(row=1, column=0, sticky=E+W)
181 self.types = []
182 self.irow = 0
183 for type in configtypes:
184 self.additem(type.name, type.color, type.enabled)
185
186 def additem(self, name, color, enabled=1):
187 item = EventConf(self.items, name, color, enabled)
188 self.types.append(item)
189 item.grid(row=self.irow, column=0, sticky=E+W)
190 self.irow += 1
191
192 def drawbuttons(self):
193 self.apply = Button(self.buttons, text="Apply",
194 command=self.apress)
195 self.revert = Button(self.buttons, text="Revert",
196 command=self.rpress)
197 self.default = Button(self.buttons, text="Default",
198 command=self.dpress)
199 self.apply.grid(row=0, column=0, sticky=E+W)
200 self.revert.grid(row=0, column=1, sticky=E+W)
201 self.default.grid(row=0, column=2, sticky=E+W)
202 self.buttons.columnconfigure(0, weight=1)
203 self.buttons.columnconfigure(1, weight=1)
204 self.buttons.columnconfigure(2, weight=1)
205
206 def apress(self):
207 for item in self.types:
208 item.apply()
209
210 def rpress(self):
211 for item in self.types:
212 item.revert()
213
214 def dpress(self):
215 for item in self.types:
216 item.default()
217
218class EventView(Toplevel):
219 def __init__(self, event, canvas):
220 Toplevel.__init__(self)
221 self.resizable(0, 0)
222 self.title("Event")
223 self.event = event
224 self.frame = Frame(self)
225 self.frame.grid(row=0, column=0, sticky=N+S+E+W)
226 self.buttons = Frame(self)
227 self.buttons.grid(row=1, column=0, sticky=E+W)
228 self.canvas = canvas
229 self.drawlabels()
230 self.drawbuttons()
231 event.displayref(canvas)
232 self.bind("<Destroy>", self.destroycb)
233
234 def destroycb(self, event):
235 self.unbind("<Destroy>")
236 if (self.event != None):
237 self.event.displayunref(self.canvas)
238 self.event = None
239 self.destroy()
240
241 def clearlabels(self):
242 for label in self.frame.grid_slaves():
243 label.grid_remove()
244
245 def drawlabels(self):
246 ypos = 0
247 labels = self.event.labels()
248 while (len(labels) < 7):
249 labels.append(("", "", 0))
250 for label in labels:
251 name, value, linked = label
252 l = Label(self.frame, text=name, bd=1, width=15,
253 relief=SUNKEN, anchor=W)
254 if (linked):
255 fgcolor = "blue"
256 else:
257 fgcolor = "black"
258 r = Label(self.frame, text=value, bd=1,
259 relief=SUNKEN, anchor=W, fg=fgcolor)
260 l.grid(row=ypos, column=0, sticky=E+W)
261 r.grid(row=ypos, column=1, sticky=E+W)
262 if (linked):
263 r.bind("<Button-1>", self.linkpress)
264 ypos += 1
265 self.frame.columnconfigure(1, minsize=80)
266
267 def drawbuttons(self):
268 self.back = Button(self.buttons, text="<", command=self.bpress)
269 self.forw = Button(self.buttons, text=">", command=self.fpress)
270 self.new = Button(self.buttons, text="new", command=self.npress)
271 self.back.grid(row=0, column=0, sticky=E+W)
272 self.forw.grid(row=0, column=1, sticky=E+W)
273 self.new.grid(row=0, column=2, sticky=E+W)
274 self.buttons.columnconfigure(2, weight=1)
275
276 def newevent(self, event):
277 self.event.displayunref(self.canvas)
278 self.clearlabels()
279 self.event = event
280 self.event.displayref(self.canvas)
281 self.drawlabels()
282
283 def npress(self):
284 EventView(self.event, self.canvas)
285
286 def bpress(self):
287 prev = self.event.prev()
288 if (prev == None):
289 return
290 while (prev.real == 0):
291 prev = prev.prev()
292 if (prev == None):
293 return
294 self.newevent(prev)
295
296 def fpress(self):
297 next = self.event.next()
298 if (next == None):
299 return
300 while (next.real == 0):
301 next = next.next()
302 if (next == None):
303 return
304 self.newevent(next)
305
306 def linkpress(self, wevent):
307 event = self.event.getlinked()
308 if (event != None):
309 self.newevent(event)
310
311class Event:
312 name = "none"
313 color = "grey"
314 def __init__(self, source, cpu, timestamp, last=0):
315 self.source = source
316 self.cpu = cpu
317 self.timestamp = int(timestamp)
318 self.entries = []
319 self.real = 1
320 self.idx = None
321 self.state = 0
322 self.item = None
323 self.dispcnt = 0
324 self.linked = None
325 if (last):
326 source.lastevent(self)
327 else:
328 source.event(self)
329
330 def status(self):
331 statstr = self.name + " " + self.source.name
332 statstr += " on: cpu" + str(self.cpu)
333 statstr += " at: " + str(self.timestamp)
334 statstr += self.stattxt()
335 status.set(statstr)
336
337 def stattxt(self):
338 return ""
339
340 def textadd(self, tuple):
341 pass
342 self.entries.append(tuple)
343
344 def labels(self):
345 return [("Source:", self.source.name, 0),
346 ("Event:", self.name, 0),
347 ("CPU:", self.cpu, 0),
348 ("Timestamp:", self.timestamp, 0)] + self.entries
349 def mouseenter(self, canvas, item):
350 self.displayref(canvas)
351 self.status()
352
353 def mouseexit(self, canvas, item):
354 self.displayunref(canvas)
355 status.clear()
356
357 def mousepress(self, canvas, item):
358 EventView(self, canvas)
359
360 def next(self):
361 return self.source.eventat(self.idx + 1)
362
363 def prev(self):
364 return self.source.eventat(self.idx - 1)
365
366 def displayref(self, canvas):
367 if (self.dispcnt == 0):
368 canvas.itemconfigure(self.item, width=2)
369 self.dispcnt += 1
370
371 def displayunref(self, canvas):
372 self.dispcnt -= 1
373 if (self.dispcnt == 0):
374 canvas.itemconfigure(self.item, width=0)
375 canvas.tag_raise("point", "state")
376
377 def getlinked(self):
378 return self.linked.findevent(self.timestamp)
379
380class PointEvent(Event):
381 def __init__(self, thread, cpu, timestamp, last=0):
382 Event.__init__(self, thread, cpu, timestamp, last)
383
384 def draw(self, canvas, xpos, ypos):
385 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
386 fill=self.color, tags=("all", "point", "event")
387 + (self.name,), width=0)
388 canvas.events[l] = self
389 self.item = l
390 if (self.enabled == 0):
391 canvas.itemconfigure(l, state="hidden")
392
393 return (xpos)
394
395class StateEvent(Event):
396 def __init__(self, thread, cpu, timestamp, last=0):
397 Event.__init__(self, thread, cpu, timestamp, last)
398 self.duration = 0
399 self.skipnext = 0
400 self.skipself = 0
401 self.state = 1
402
403 def draw(self, canvas, xpos, ypos):
404 next = self.nextstate()
405 if (self.skipself == 1 or next == None):
406 return (xpos)
407 while (self.skipnext):
408 skipped = next
409 next.skipself = 1
410 next.real = 0
411 next = next.nextstate()
412 if (next == None):
413 next = skipped
414 self.skipnext -= 1
415 self.duration = next.timestamp - self.timestamp
416 delta = self.duration / canvas.ratio
417 l = canvas.create_rectangle(xpos, ypos,
418 xpos + delta, ypos - 10, fill=self.color, width=0,
419 tags=("all", "state", "event") + (self.name,))
420 canvas.events[l] = self
421 self.item = l
422 if (self.enabled == 0):
423 canvas.itemconfigure(l, state="hidden")
424
425 return (xpos + delta)
426
427 def stattxt(self):
428 return " duration: " + ticks2sec(self.duration)
429
430 def nextstate(self):
431 next = self.next()
432 while (next != None and next.state == 0):
433 next = next.next()
434 return (next)
435
436 def labels(self):
437 return [("Source:", self.source.name, 0),
438 ("Event:", self.name, 0),
439 ("Timestamp:", self.timestamp, 0),
440 ("CPU:", self.cpu, 0),
441 ("Duration:", ticks2sec(self.duration), 0)] \
442 + self.entries
443
444class Count(Event):
445 name = "Count"
446 color = "red"
447 enabled = 1
448 def __init__(self, source, cpu, timestamp, count):
449 self.count = int(count)
450 Event.__init__(self, source, cpu, timestamp)
451 self.duration = 0
452 self.textadd(("count:", self.count, 0))
453
454 def draw(self, canvas, xpos, ypos):
455 next = self.next()
456 self.duration = next.timestamp - self.timestamp
457 delta = self.duration / canvas.ratio
458 yhight = self.source.yscale() * self.count
459 l = canvas.create_rectangle(xpos, ypos - yhight,
460 xpos + delta, ypos, fill=self.color, width=0,
461 tags=("all", "count", "event") + (self.name,))
462 canvas.events[l] = self
463 self.item = l
464 if (self.enabled == 0):
465 canvas.itemconfigure(l, state="hidden")
466 return (xpos + delta)
467
468 def stattxt(self):
469 return " count: " + str(self.count)
470
471configtypes.append(Count)
472
473class Running(StateEvent):
474 name = "running"
475 color = "green"
476 enabled = 1
477 def __init__(self, thread, cpu, timestamp, prio):
478 StateEvent.__init__(self, thread, cpu, timestamp)
479 self.prio = prio
480 self.textadd(("prio:", self.prio, 0))
481
482configtypes.append(Running)
483
484class Idle(StateEvent):
485 name = "idle"
486 color = "grey"
487 enabled = 0
488 def __init__(self, thread, cpu, timestamp, prio):
489 StateEvent.__init__(self, thread, cpu, timestamp)
490 self.prio = prio
491 self.textadd(("prio:", self.prio, 0))
492
493configtypes.append(Idle)
494
495class Yielding(StateEvent):
496 name = "yielding"
497 color = "yellow"
498 enabled = 1
499 def __init__(self, thread, cpu, timestamp, prio):
500 StateEvent.__init__(self, thread, cpu, timestamp)
501 self.skipnext = 2
502 self.prio = prio
503 self.textadd(("prio:", self.prio, 0))
504
505configtypes.append(Yielding)
506
507class Swapped(StateEvent):
508 name = "swapped"
509 color = "violet"
510 enabled = 1
511 def __init__(self, thread, cpu, timestamp, prio):
512 StateEvent.__init__(self, thread, cpu, timestamp)
513 self.prio = prio
514 self.textadd(("prio:", self.prio, 0))
515
516configtypes.append(Swapped)
517
518class Suspended(StateEvent):
519 name = "suspended"
520 color = "purple"
521 enabled = 1
522 def __init__(self, thread, cpu, timestamp, prio):
523 StateEvent.__init__(self, thread, cpu, timestamp)
524 self.prio = prio
525 self.textadd(("prio:", self.prio, 0))
526
527configtypes.append(Suspended)
528
529class Iwait(StateEvent):
530 name = "iwait"
531 color = "grey"
532 enabled = 0
533 def __init__(self, thread, cpu, timestamp, prio):
534 StateEvent.__init__(self, thread, cpu, timestamp)
535 self.prio = prio
536 self.textadd(("prio:", self.prio, 0))
537
538configtypes.append(Iwait)
539
540class Preempted(StateEvent):
541 name = "preempted"
542 color = "red"
543 enabled = 1
544 def __init__(self, thread, cpu, timestamp, prio, bythread):
545 StateEvent.__init__(self, thread, cpu, timestamp)
546 self.skipnext = 2
547 self.prio = prio
548 self.linked = bythread
549 self.textadd(("prio:", self.prio, 0))
550 self.textadd(("by thread:", self.linked.name, 1))
551
552configtypes.append(Preempted)
553
554class Sleep(StateEvent):
555 name = "sleep"
556 color = "blue"
557 enabled = 1
558 def __init__(self, thread, cpu, timestamp, prio, wmesg):
559 StateEvent.__init__(self, thread, cpu, timestamp)
560 self.prio = prio
561 self.wmesg = wmesg
562 self.textadd(("prio:", self.prio, 0))
563 self.textadd(("wmesg:", self.wmesg, 0))
564
565 def stattxt(self):
566 statstr = StateEvent.stattxt(self)
567 statstr += " sleeping on: " + self.wmesg
568 return (statstr)
569
570configtypes.append(Sleep)
571
572class Blocked(StateEvent):
573 name = "blocked"
574 color = "dark red"
575 enabled = 1
576 def __init__(self, thread, cpu, timestamp, prio, lock):
577 StateEvent.__init__(self, thread, cpu, timestamp)
578 self.prio = prio
579 self.lock = lock
580 self.textadd(("prio:", self.prio, 0))
581 self.textadd(("lock:", self.lock, 0))
582
583 def stattxt(self):
584 statstr = StateEvent.stattxt(self)
585 statstr += " blocked on: " + self.lock
586 return (statstr)
587
588configtypes.append(Blocked)
589
590class KsegrpRunq(StateEvent):
591 name = "KsegrpRunq"
592 color = "orange"
593 enabled = 1
594 def __init__(self, thread, cpu, timestamp, prio, bythread):
595 StateEvent.__init__(self, thread, cpu, timestamp)
596 self.prio = prio
597 self.linked = bythread
598 self.textadd(("prio:", self.prio, 0))
599 self.textadd(("by thread:", self.linked.name, 1))
600
601configtypes.append(KsegrpRunq)
602
603class Runq(StateEvent):
604 name = "Runq"
605 color = "yellow"
606 enabled = 1
607 def __init__(self, thread, cpu, timestamp, prio, bythread):
608 StateEvent.__init__(self, thread, cpu, timestamp)
609 self.prio = prio
610 self.linked = bythread
611 self.textadd(("prio:", self.prio, 0))
612 self.textadd(("by thread:", self.linked.name, 1))
613
614configtypes.append(Runq)
615
616class Sched_exit(StateEvent):
617 name = "exit"
618 color = "grey"
619 enabled = 0
620 def __init__(self, thread, cpu, timestamp, prio):
621 StateEvent.__init__(self, thread, cpu, timestamp)
622 self.name = "sched_exit"
623 self.prio = prio
624 self.textadd(("prio:", self.prio, 0))
625
626configtypes.append(Sched_exit)
627
628class Padevent(StateEvent):
629 def __init__(self, thread, cpu, timestamp, last=0):
630 StateEvent.__init__(self, thread, cpu, timestamp, last)
631 self.name = "pad"
632 self.real = 0
633
634 def draw(self, canvas, xpos, ypos):
635 next = self.next()
636 if (next == None):
637 return (xpos)
638 self.duration = next.timestamp - self.timestamp
639 delta = self.duration / canvas.ratio
640 return (xpos + delta)
641
642class Tick(PointEvent):
643 name = "tick"
644 color = "black"
645 enabled = 0
646 def __init__(self, thread, cpu, timestamp, prio, stathz):
647 PointEvent.__init__(self, thread, cpu, timestamp)
648 self.prio = prio
649 self.textadd(("prio:", self.prio, 0))
650
651configtypes.append(Tick)
652
653class Prio(PointEvent):
654 name = "prio"
655 color = "black"
656 enabled = 0
657 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
658 PointEvent.__init__(self, thread, cpu, timestamp)
659 self.prio = prio
660 self.newprio = newprio
661 self.linked = bythread
662 self.textadd(("new prio:", self.newprio, 0))
663 self.textadd(("prio:", self.prio, 0))
664 if (self.linked != self.source):
665 self.textadd(("by thread:", self.linked.name, 1))
666 else:
667 self.textadd(("by thread:", self.linked.name, 0))
668
669configtypes.append(Prio)
670
671class Lend(PointEvent):
672 name = "lend"
673 color = "black"
674 enabled = 0
675 def __init__(self, thread, cpu, timestamp, prio, tothread):
676 PointEvent.__init__(self, thread, cpu, timestamp)
677 self.prio = prio
678 self.linked = tothread
679 self.textadd(("prio:", self.prio, 0))
680 self.textadd(("to thread:", self.linked.name, 1))
681
682configtypes.append(Lend)
683
684class Wokeup(PointEvent):
685 name = "wokeup"
686 color = "black"
687 enabled = 0
688 def __init__(self, thread, cpu, timestamp, ranthread):
689 PointEvent.__init__(self, thread, cpu, timestamp)
690 self.linked = ranthread
691 self.textadd(("ran thread:", self.linked.name, 1))
692
693configtypes.append(Wokeup)
694
695class EventSource:
696 def __init__(self, name):
697 self.name = name
698 self.events = []
699 self.cpu = 0
700 self.cpux = 0
701
702 def fixup(self):
703 pass
704
705 def event(self, event):
706 self.events.insert(0, event)
707
708 def remove(self, event):
709 self.events.remove(event)
710
711 def lastevent(self, event):
712 self.events.append(event)
713
714 def draw(self, canvas, ypos):
715 xpos = 10
716 self.cpux = 10
717 self.cpu = self.events[1].cpu
718 for i in range(0, len(self.events)):
719 self.events[i].idx = i
720 for event in self.events:
721 if (event.cpu != self.cpu and event.cpu != -1):
722 self.drawcpu(canvas, xpos, ypos)
723 self.cpux = xpos
724 self.cpu = event.cpu
725 xpos = event.draw(canvas, xpos, ypos)
726 self.drawcpu(canvas, xpos, ypos)
727
728 def drawname(self, canvas, ypos):
729 ypos = ypos - (self.ysize() / 2)
730 canvas.create_text(10, ypos, anchor="w", text=self.name)
731
732 def drawcpu(self, canvas, xpos, ypos):
733 cpu = int(self.cpu)
734 if (cpu == 0):
735 color = 'light grey'
736 elif (cpu == 1):
737 color = 'dark grey'
738 elif (cpu == 2):
739 color = 'light blue'
740 elif (cpu == 3):
28
29import sys
30import re
31from Tkinter import *
32
33# To use:
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'
41#
42# To do:
43# 1) Add a per-thread summary display
44# 2) Add bounding box style zoom.
45# 3) Click to center.
46# 4) Implement some sorting mechanism.
47
48ticksps = None
49status = None
50configtypes = []
51
52def ticks2sec(ticks):
53 ns = ticksps / 1000000000
54 ticks /= ns
55 if (ticks < 1000):
56 return (str(ticks) + "ns")
57 ticks /= 1000
58 if (ticks < 1000):
59 return (str(ticks) + "us")
60 ticks /= 1000
61 if (ticks < 1000):
62 return (str(ticks) + "ms")
63 ticks /= 1000
64 return (str(ticks) + "s")
65
66class Scaler(Frame):
67 def __init__(self, master, target):
68 Frame.__init__(self, master)
69 self.scale = Scale(self, command=self.scaleset,
70 from_=1000, to_=1000000, orient=HORIZONTAL, resolution=1000)
71 self.label = Label(self, text="Ticks per pixel")
72 self.label.pack(side=LEFT)
73 self.scale.pack(fill="both", expand=1)
74 self.target = target
75 self.scale.set(target.scaleget())
76 self.initialized = 1
77
78 def scaleset(self, value):
79 self.target.scaleset(int(value))
80
81 def set(self, value):
82 self.scale.set(value)
83
84class Status(Frame):
85 def __init__(self, master):
86 Frame.__init__(self, master)
87 self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
88 self.label.pack(fill="both", expand=1)
89 self.clear()
90
91 def set(self, str):
92 self.label.config(text=str)
93
94 def clear(self):
95 self.label.config(text="")
96
97 def startup(self, str):
98 self.set(str)
99 root.update()
100
101class EventConf(Frame):
102 def __init__(self, master, name, color, enabled):
103 Frame.__init__(self, master)
104 self.name = name
105 self.color = StringVar()
106 self.color_default = color
107 self.color_current = color
108 self.color.set(color)
109 self.enabled = IntVar()
110 self.enabled_default = enabled
111 self.enabled_current = enabled
112 self.enabled.set(enabled)
113 self.draw()
114
115 def draw(self):
116 self.label = Label(self, text=self.name, anchor=W)
117 self.sample = Canvas(self, width=24, height=24,
118 bg='grey')
119 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
120 fill=self.color.get())
121 self.list = OptionMenu(self, self.color,
122 "dark red", "red", "pink",
123 "dark orange", "orange",
124 "yellow", "light yellow",
125 "dark green", "green", "light green",
126 "dark blue", "blue", "light blue",
127 "dark violet", "violet", "purple",
128 "dark grey", "light grey",
129 "white", "black",
130 command=self.setcolor)
131 self.checkbox = Checkbutton(self, text="enabled",
132 variable=self.enabled)
133 self.label.grid(row=0, column=0, sticky=E+W)
134 self.sample.grid(row=0, column=1)
135 self.list.grid(row=0, column=2, sticky=E+W)
136 self.checkbox.grid(row=0, column=3)
137 self.columnconfigure(0, weight=1)
138 self.columnconfigure(2, minsize=110)
139
140 def setcolor(self, color):
141 self.color.set(color)
142 self.sample.itemconfigure(self.rect, fill=color)
143
144 def apply(self):
145 cchange = 0
146 echange = 0
147 if (self.color_current != self.color.get()):
148 cchange = 1
149 if (self.enabled_current != self.enabled.get()):
150 echange = 1
151 self.color_current = self.color.get()
152 self.enabled_current = self.enabled.get()
153 if (echange != 0):
154 if (self.enabled_current):
155 graph.setcolor(self.name, self.color_current)
156 else:
157 graph.hide(self.name)
158 return
159 if (cchange != 0):
160 graph.setcolor(self.name, self.color_current)
161
162 def revert(self):
163 self.setcolor(self.color_current)
164 self.enabled.set(self.enabled_current)
165
166 def default(self):
167 self.setcolor(self.color_default)
168 self.enabled.set(self.enabled_default)
169
170class EventConfigure(Toplevel):
171 def __init__(self):
172 Toplevel.__init__(self)
173 self.resizable(0, 0)
174 self.title("Event Configuration")
175 self.items = LabelFrame(self, text="Event Type")
176 self.buttons = Frame(self)
177 self.drawbuttons()
178 self.items.grid(row=0, column=0, sticky=E+W)
179 self.columnconfigure(0, weight=1)
180 self.buttons.grid(row=1, column=0, sticky=E+W)
181 self.types = []
182 self.irow = 0
183 for type in configtypes:
184 self.additem(type.name, type.color, type.enabled)
185
186 def additem(self, name, color, enabled=1):
187 item = EventConf(self.items, name, color, enabled)
188 self.types.append(item)
189 item.grid(row=self.irow, column=0, sticky=E+W)
190 self.irow += 1
191
192 def drawbuttons(self):
193 self.apply = Button(self.buttons, text="Apply",
194 command=self.apress)
195 self.revert = Button(self.buttons, text="Revert",
196 command=self.rpress)
197 self.default = Button(self.buttons, text="Default",
198 command=self.dpress)
199 self.apply.grid(row=0, column=0, sticky=E+W)
200 self.revert.grid(row=0, column=1, sticky=E+W)
201 self.default.grid(row=0, column=2, sticky=E+W)
202 self.buttons.columnconfigure(0, weight=1)
203 self.buttons.columnconfigure(1, weight=1)
204 self.buttons.columnconfigure(2, weight=1)
205
206 def apress(self):
207 for item in self.types:
208 item.apply()
209
210 def rpress(self):
211 for item in self.types:
212 item.revert()
213
214 def dpress(self):
215 for item in self.types:
216 item.default()
217
218class EventView(Toplevel):
219 def __init__(self, event, canvas):
220 Toplevel.__init__(self)
221 self.resizable(0, 0)
222 self.title("Event")
223 self.event = event
224 self.frame = Frame(self)
225 self.frame.grid(row=0, column=0, sticky=N+S+E+W)
226 self.buttons = Frame(self)
227 self.buttons.grid(row=1, column=0, sticky=E+W)
228 self.canvas = canvas
229 self.drawlabels()
230 self.drawbuttons()
231 event.displayref(canvas)
232 self.bind("<Destroy>", self.destroycb)
233
234 def destroycb(self, event):
235 self.unbind("<Destroy>")
236 if (self.event != None):
237 self.event.displayunref(self.canvas)
238 self.event = None
239 self.destroy()
240
241 def clearlabels(self):
242 for label in self.frame.grid_slaves():
243 label.grid_remove()
244
245 def drawlabels(self):
246 ypos = 0
247 labels = self.event.labels()
248 while (len(labels) < 7):
249 labels.append(("", "", 0))
250 for label in labels:
251 name, value, linked = label
252 l = Label(self.frame, text=name, bd=1, width=15,
253 relief=SUNKEN, anchor=W)
254 if (linked):
255 fgcolor = "blue"
256 else:
257 fgcolor = "black"
258 r = Label(self.frame, text=value, bd=1,
259 relief=SUNKEN, anchor=W, fg=fgcolor)
260 l.grid(row=ypos, column=0, sticky=E+W)
261 r.grid(row=ypos, column=1, sticky=E+W)
262 if (linked):
263 r.bind("<Button-1>", self.linkpress)
264 ypos += 1
265 self.frame.columnconfigure(1, minsize=80)
266
267 def drawbuttons(self):
268 self.back = Button(self.buttons, text="<", command=self.bpress)
269 self.forw = Button(self.buttons, text=">", command=self.fpress)
270 self.new = Button(self.buttons, text="new", command=self.npress)
271 self.back.grid(row=0, column=0, sticky=E+W)
272 self.forw.grid(row=0, column=1, sticky=E+W)
273 self.new.grid(row=0, column=2, sticky=E+W)
274 self.buttons.columnconfigure(2, weight=1)
275
276 def newevent(self, event):
277 self.event.displayunref(self.canvas)
278 self.clearlabels()
279 self.event = event
280 self.event.displayref(self.canvas)
281 self.drawlabels()
282
283 def npress(self):
284 EventView(self.event, self.canvas)
285
286 def bpress(self):
287 prev = self.event.prev()
288 if (prev == None):
289 return
290 while (prev.real == 0):
291 prev = prev.prev()
292 if (prev == None):
293 return
294 self.newevent(prev)
295
296 def fpress(self):
297 next = self.event.next()
298 if (next == None):
299 return
300 while (next.real == 0):
301 next = next.next()
302 if (next == None):
303 return
304 self.newevent(next)
305
306 def linkpress(self, wevent):
307 event = self.event.getlinked()
308 if (event != None):
309 self.newevent(event)
310
311class Event:
312 name = "none"
313 color = "grey"
314 def __init__(self, source, cpu, timestamp, last=0):
315 self.source = source
316 self.cpu = cpu
317 self.timestamp = int(timestamp)
318 self.entries = []
319 self.real = 1
320 self.idx = None
321 self.state = 0
322 self.item = None
323 self.dispcnt = 0
324 self.linked = None
325 if (last):
326 source.lastevent(self)
327 else:
328 source.event(self)
329
330 def status(self):
331 statstr = self.name + " " + self.source.name
332 statstr += " on: cpu" + str(self.cpu)
333 statstr += " at: " + str(self.timestamp)
334 statstr += self.stattxt()
335 status.set(statstr)
336
337 def stattxt(self):
338 return ""
339
340 def textadd(self, tuple):
341 pass
342 self.entries.append(tuple)
343
344 def labels(self):
345 return [("Source:", self.source.name, 0),
346 ("Event:", self.name, 0),
347 ("CPU:", self.cpu, 0),
348 ("Timestamp:", self.timestamp, 0)] + self.entries
349 def mouseenter(self, canvas, item):
350 self.displayref(canvas)
351 self.status()
352
353 def mouseexit(self, canvas, item):
354 self.displayunref(canvas)
355 status.clear()
356
357 def mousepress(self, canvas, item):
358 EventView(self, canvas)
359
360 def next(self):
361 return self.source.eventat(self.idx + 1)
362
363 def prev(self):
364 return self.source.eventat(self.idx - 1)
365
366 def displayref(self, canvas):
367 if (self.dispcnt == 0):
368 canvas.itemconfigure(self.item, width=2)
369 self.dispcnt += 1
370
371 def displayunref(self, canvas):
372 self.dispcnt -= 1
373 if (self.dispcnt == 0):
374 canvas.itemconfigure(self.item, width=0)
375 canvas.tag_raise("point", "state")
376
377 def getlinked(self):
378 return self.linked.findevent(self.timestamp)
379
380class PointEvent(Event):
381 def __init__(self, thread, cpu, timestamp, last=0):
382 Event.__init__(self, thread, cpu, timestamp, last)
383
384 def draw(self, canvas, xpos, ypos):
385 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
386 fill=self.color, tags=("all", "point", "event")
387 + (self.name,), width=0)
388 canvas.events[l] = self
389 self.item = l
390 if (self.enabled == 0):
391 canvas.itemconfigure(l, state="hidden")
392
393 return (xpos)
394
395class StateEvent(Event):
396 def __init__(self, thread, cpu, timestamp, last=0):
397 Event.__init__(self, thread, cpu, timestamp, last)
398 self.duration = 0
399 self.skipnext = 0
400 self.skipself = 0
401 self.state = 1
402
403 def draw(self, canvas, xpos, ypos):
404 next = self.nextstate()
405 if (self.skipself == 1 or next == None):
406 return (xpos)
407 while (self.skipnext):
408 skipped = next
409 next.skipself = 1
410 next.real = 0
411 next = next.nextstate()
412 if (next == None):
413 next = skipped
414 self.skipnext -= 1
415 self.duration = next.timestamp - self.timestamp
416 delta = self.duration / canvas.ratio
417 l = canvas.create_rectangle(xpos, ypos,
418 xpos + delta, ypos - 10, fill=self.color, width=0,
419 tags=("all", "state", "event") + (self.name,))
420 canvas.events[l] = self
421 self.item = l
422 if (self.enabled == 0):
423 canvas.itemconfigure(l, state="hidden")
424
425 return (xpos + delta)
426
427 def stattxt(self):
428 return " duration: " + ticks2sec(self.duration)
429
430 def nextstate(self):
431 next = self.next()
432 while (next != None and next.state == 0):
433 next = next.next()
434 return (next)
435
436 def labels(self):
437 return [("Source:", self.source.name, 0),
438 ("Event:", self.name, 0),
439 ("Timestamp:", self.timestamp, 0),
440 ("CPU:", self.cpu, 0),
441 ("Duration:", ticks2sec(self.duration), 0)] \
442 + self.entries
443
444class Count(Event):
445 name = "Count"
446 color = "red"
447 enabled = 1
448 def __init__(self, source, cpu, timestamp, count):
449 self.count = int(count)
450 Event.__init__(self, source, cpu, timestamp)
451 self.duration = 0
452 self.textadd(("count:", self.count, 0))
453
454 def draw(self, canvas, xpos, ypos):
455 next = self.next()
456 self.duration = next.timestamp - self.timestamp
457 delta = self.duration / canvas.ratio
458 yhight = self.source.yscale() * self.count
459 l = canvas.create_rectangle(xpos, ypos - yhight,
460 xpos + delta, ypos, fill=self.color, width=0,
461 tags=("all", "count", "event") + (self.name,))
462 canvas.events[l] = self
463 self.item = l
464 if (self.enabled == 0):
465 canvas.itemconfigure(l, state="hidden")
466 return (xpos + delta)
467
468 def stattxt(self):
469 return " count: " + str(self.count)
470
471configtypes.append(Count)
472
473class Running(StateEvent):
474 name = "running"
475 color = "green"
476 enabled = 1
477 def __init__(self, thread, cpu, timestamp, prio):
478 StateEvent.__init__(self, thread, cpu, timestamp)
479 self.prio = prio
480 self.textadd(("prio:", self.prio, 0))
481
482configtypes.append(Running)
483
484class Idle(StateEvent):
485 name = "idle"
486 color = "grey"
487 enabled = 0
488 def __init__(self, thread, cpu, timestamp, prio):
489 StateEvent.__init__(self, thread, cpu, timestamp)
490 self.prio = prio
491 self.textadd(("prio:", self.prio, 0))
492
493configtypes.append(Idle)
494
495class Yielding(StateEvent):
496 name = "yielding"
497 color = "yellow"
498 enabled = 1
499 def __init__(self, thread, cpu, timestamp, prio):
500 StateEvent.__init__(self, thread, cpu, timestamp)
501 self.skipnext = 2
502 self.prio = prio
503 self.textadd(("prio:", self.prio, 0))
504
505configtypes.append(Yielding)
506
507class Swapped(StateEvent):
508 name = "swapped"
509 color = "violet"
510 enabled = 1
511 def __init__(self, thread, cpu, timestamp, prio):
512 StateEvent.__init__(self, thread, cpu, timestamp)
513 self.prio = prio
514 self.textadd(("prio:", self.prio, 0))
515
516configtypes.append(Swapped)
517
518class Suspended(StateEvent):
519 name = "suspended"
520 color = "purple"
521 enabled = 1
522 def __init__(self, thread, cpu, timestamp, prio):
523 StateEvent.__init__(self, thread, cpu, timestamp)
524 self.prio = prio
525 self.textadd(("prio:", self.prio, 0))
526
527configtypes.append(Suspended)
528
529class Iwait(StateEvent):
530 name = "iwait"
531 color = "grey"
532 enabled = 0
533 def __init__(self, thread, cpu, timestamp, prio):
534 StateEvent.__init__(self, thread, cpu, timestamp)
535 self.prio = prio
536 self.textadd(("prio:", self.prio, 0))
537
538configtypes.append(Iwait)
539
540class Preempted(StateEvent):
541 name = "preempted"
542 color = "red"
543 enabled = 1
544 def __init__(self, thread, cpu, timestamp, prio, bythread):
545 StateEvent.__init__(self, thread, cpu, timestamp)
546 self.skipnext = 2
547 self.prio = prio
548 self.linked = bythread
549 self.textadd(("prio:", self.prio, 0))
550 self.textadd(("by thread:", self.linked.name, 1))
551
552configtypes.append(Preempted)
553
554class Sleep(StateEvent):
555 name = "sleep"
556 color = "blue"
557 enabled = 1
558 def __init__(self, thread, cpu, timestamp, prio, wmesg):
559 StateEvent.__init__(self, thread, cpu, timestamp)
560 self.prio = prio
561 self.wmesg = wmesg
562 self.textadd(("prio:", self.prio, 0))
563 self.textadd(("wmesg:", self.wmesg, 0))
564
565 def stattxt(self):
566 statstr = StateEvent.stattxt(self)
567 statstr += " sleeping on: " + self.wmesg
568 return (statstr)
569
570configtypes.append(Sleep)
571
572class Blocked(StateEvent):
573 name = "blocked"
574 color = "dark red"
575 enabled = 1
576 def __init__(self, thread, cpu, timestamp, prio, lock):
577 StateEvent.__init__(self, thread, cpu, timestamp)
578 self.prio = prio
579 self.lock = lock
580 self.textadd(("prio:", self.prio, 0))
581 self.textadd(("lock:", self.lock, 0))
582
583 def stattxt(self):
584 statstr = StateEvent.stattxt(self)
585 statstr += " blocked on: " + self.lock
586 return (statstr)
587
588configtypes.append(Blocked)
589
590class KsegrpRunq(StateEvent):
591 name = "KsegrpRunq"
592 color = "orange"
593 enabled = 1
594 def __init__(self, thread, cpu, timestamp, prio, bythread):
595 StateEvent.__init__(self, thread, cpu, timestamp)
596 self.prio = prio
597 self.linked = bythread
598 self.textadd(("prio:", self.prio, 0))
599 self.textadd(("by thread:", self.linked.name, 1))
600
601configtypes.append(KsegrpRunq)
602
603class Runq(StateEvent):
604 name = "Runq"
605 color = "yellow"
606 enabled = 1
607 def __init__(self, thread, cpu, timestamp, prio, bythread):
608 StateEvent.__init__(self, thread, cpu, timestamp)
609 self.prio = prio
610 self.linked = bythread
611 self.textadd(("prio:", self.prio, 0))
612 self.textadd(("by thread:", self.linked.name, 1))
613
614configtypes.append(Runq)
615
616class Sched_exit(StateEvent):
617 name = "exit"
618 color = "grey"
619 enabled = 0
620 def __init__(self, thread, cpu, timestamp, prio):
621 StateEvent.__init__(self, thread, cpu, timestamp)
622 self.name = "sched_exit"
623 self.prio = prio
624 self.textadd(("prio:", self.prio, 0))
625
626configtypes.append(Sched_exit)
627
628class Padevent(StateEvent):
629 def __init__(self, thread, cpu, timestamp, last=0):
630 StateEvent.__init__(self, thread, cpu, timestamp, last)
631 self.name = "pad"
632 self.real = 0
633
634 def draw(self, canvas, xpos, ypos):
635 next = self.next()
636 if (next == None):
637 return (xpos)
638 self.duration = next.timestamp - self.timestamp
639 delta = self.duration / canvas.ratio
640 return (xpos + delta)
641
642class Tick(PointEvent):
643 name = "tick"
644 color = "black"
645 enabled = 0
646 def __init__(self, thread, cpu, timestamp, prio, stathz):
647 PointEvent.__init__(self, thread, cpu, timestamp)
648 self.prio = prio
649 self.textadd(("prio:", self.prio, 0))
650
651configtypes.append(Tick)
652
653class Prio(PointEvent):
654 name = "prio"
655 color = "black"
656 enabled = 0
657 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
658 PointEvent.__init__(self, thread, cpu, timestamp)
659 self.prio = prio
660 self.newprio = newprio
661 self.linked = bythread
662 self.textadd(("new prio:", self.newprio, 0))
663 self.textadd(("prio:", self.prio, 0))
664 if (self.linked != self.source):
665 self.textadd(("by thread:", self.linked.name, 1))
666 else:
667 self.textadd(("by thread:", self.linked.name, 0))
668
669configtypes.append(Prio)
670
671class Lend(PointEvent):
672 name = "lend"
673 color = "black"
674 enabled = 0
675 def __init__(self, thread, cpu, timestamp, prio, tothread):
676 PointEvent.__init__(self, thread, cpu, timestamp)
677 self.prio = prio
678 self.linked = tothread
679 self.textadd(("prio:", self.prio, 0))
680 self.textadd(("to thread:", self.linked.name, 1))
681
682configtypes.append(Lend)
683
684class Wokeup(PointEvent):
685 name = "wokeup"
686 color = "black"
687 enabled = 0
688 def __init__(self, thread, cpu, timestamp, ranthread):
689 PointEvent.__init__(self, thread, cpu, timestamp)
690 self.linked = ranthread
691 self.textadd(("ran thread:", self.linked.name, 1))
692
693configtypes.append(Wokeup)
694
695class EventSource:
696 def __init__(self, name):
697 self.name = name
698 self.events = []
699 self.cpu = 0
700 self.cpux = 0
701
702 def fixup(self):
703 pass
704
705 def event(self, event):
706 self.events.insert(0, event)
707
708 def remove(self, event):
709 self.events.remove(event)
710
711 def lastevent(self, event):
712 self.events.append(event)
713
714 def draw(self, canvas, ypos):
715 xpos = 10
716 self.cpux = 10
717 self.cpu = self.events[1].cpu
718 for i in range(0, len(self.events)):
719 self.events[i].idx = i
720 for event in self.events:
721 if (event.cpu != self.cpu and event.cpu != -1):
722 self.drawcpu(canvas, xpos, ypos)
723 self.cpux = xpos
724 self.cpu = event.cpu
725 xpos = event.draw(canvas, xpos, ypos)
726 self.drawcpu(canvas, xpos, ypos)
727
728 def drawname(self, canvas, ypos):
729 ypos = ypos - (self.ysize() / 2)
730 canvas.create_text(10, ypos, anchor="w", text=self.name)
731
732 def drawcpu(self, canvas, xpos, ypos):
733 cpu = int(self.cpu)
734 if (cpu == 0):
735 color = 'light grey'
736 elif (cpu == 1):
737 color = 'dark grey'
738 elif (cpu == 2):
739 color = 'light blue'
740 elif (cpu == 3):
741 color == 'light green'
741 color = 'light green'
742 else:
742 else:
743 color == "white"
743 color = "white"
744 l = canvas.create_rectangle(self.cpux,
745 ypos - self.ysize() - canvas.bdheight,
746 xpos, ypos + canvas.bdheight, fill=color, width=0,
747 tags=("all", "cpuinfo"))
748
749 def ysize(self):
750 return (None)
751
752 def eventat(self, i):
753 if (i >= len(self.events)):
754 return (None)
755 event = self.events[i]
756 return (event)
757
758 def findevent(self, timestamp):
759 for event in self.events:
760 if (event.timestamp >= timestamp and event.real):
761 return (event)
762 return (None)
763
764class Thread(EventSource):
765 names = {}
766 def __init__(self, td, pcomm):
767 EventSource.__init__(self, pcomm)
768 self.str = td
769 try:
770 cnt = Thread.names[pcomm]
771 except:
772 Thread.names[pcomm] = 0
773 return
774 Thread.names[pcomm] = cnt + 1
775
776 def fixup(self):
777 cnt = Thread.names[self.name]
778 if (cnt == 0):
779 return
780 cnt -= 1
781 Thread.names[self.name] = cnt
782 self.name += " td" + str(cnt)
783
784 def ysize(self):
785 return (10)
786
787class Counter(EventSource):
788 max = 0
789 def __init__(self, name):
790 EventSource.__init__(self, name)
791
792 def event(self, event):
793 EventSource.event(self, event)
794 try:
795 count = event.count
796 except:
797 return
798 count = int(count)
799 if (count > Counter.max):
800 Counter.max = count
801
802 def ysize(self):
803 return (80)
804
805 def yscale(self):
806 return (self.ysize() / Counter.max)
807
808
809class KTRFile:
810 def __init__(self, file):
811 self.timestamp_first = None
812 self.timestamp_last = None
813 self.lineno = -1
814 self.threads = []
815 self.sources = []
816 self.ticks = {}
817 self.load = {}
818
819 self.parse(file)
820 self.fixup()
821 global ticksps
822 ticksps = self.ticksps()
823
824 def parse(self, file):
825 try:
826 ifp = open(file)
827 except:
828 print "Can't open", file
829 sys.exit(1)
830
831 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+"
832 tdname = "(\S+)\(([^)]*)\)"
833
834 ktrstr = "mi_switch: " + tdname
835 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
836 switchout_re = re.compile(ktrhdr + ktrstr)
837
838 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
839 idled_re = re.compile(ktrhdr + ktrstr)
840
841 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
842 ktrstr += tdname
843 preempted_re = re.compile(ktrhdr + ktrstr)
844
845 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
846 switchin_re = re.compile(ktrhdr + ktrstr)
847
848 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
849 sched_add_re = re.compile(ktrhdr + ktrstr)
850
851 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
852 setrunqueue_re = re.compile(ktrhdr + ktrstr)
853
854 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
855 sched_rem_re = re.compile(ktrhdr + ktrstr)
856
857 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
858 sched_exit_re = re.compile(ktrhdr + ktrstr)
859
860 ktrstr = "statclock: " + tdname + " prio (\d+)"
861 ktrstr += " stathz (\d+)"
862 sched_clock_re = re.compile(ktrhdr + ktrstr)
863
864 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
865 ktrstr += " newprio (\d+) by " + tdname
866 sched_prio_re = re.compile(ktrhdr + ktrstr)
867
868 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
869 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
870
871 parsers = [[cpuload_re, self.cpuload],
872 [loadglobal_re, self.loadglobal],
873 [switchin_re, self.switchin],
874 [switchout_re, self.switchout],
875 [sched_add_re, self.sched_add],
876 [setrunqueue_re, self.sched_rem],
877 [sched_prio_re, self.sched_prio],
878 [preempted_re, self.preempted],
879 [sched_rem_re, self.sched_rem],
880 [sched_exit_re, self.sched_exit],
881 [sched_clock_re, self.sched_clock],
882 [idled_re, self.idled]]
883
884 for line in ifp.readlines():
885 self.lineno += 1
886 if ((self.lineno % 1024) == 0):
887 status.startup("Parsing line " +
888 str(self.lineno))
889 for p in parsers:
890 m = p[0].match(line)
891 if (m != None):
892 p[1](*m.groups())
893 break
894 # if (m == None):
895 # print line,
896
897 def checkstamp(self, timestamp):
898 timestamp = int(timestamp)
899 if (self.timestamp_first == None):
900 self.timestamp_first = timestamp
901 if (timestamp > self.timestamp_first):
902 print "Bad timestamp on line ", self.lineno
903 return (0)
904 self.timestamp_last = timestamp
905 return (1)
906
907 def timespan(self):
908 return (self.timestamp_first - self.timestamp_last);
909
910 def ticksps(self):
911 return (self.timespan() / self.ticks[0]) * int(self.stathz)
912
913 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
914 TDI_SUSPENDED = 0x0001
915 TDI_SLEEPING = 0x0002
916 TDI_SWAPPED = 0x0004
917 TDI_LOCK = 0x0008
918 TDI_IWAIT = 0x0010
919
920 if (self.checkstamp(timestamp) == 0):
921 return
922 inhibit = int(inhibit)
923 thread = self.findtd(td, pcomm)
924 if (inhibit & TDI_SWAPPED):
925 Swapped(thread, cpu, timestamp, prio)
926 elif (inhibit & TDI_SLEEPING):
927 Sleep(thread, cpu, timestamp, prio, wmesg)
928 elif (inhibit & TDI_LOCK):
929 Blocked(thread, cpu, timestamp, prio, lock)
930 elif (inhibit & TDI_IWAIT):
931 Iwait(thread, cpu, timestamp, prio)
932 elif (inhibit & TDI_SUSPENDED):
933 Suspended(thread, cpu, timestamp, prio)
934 elif (inhibit == 0):
935 Yielding(thread, cpu, timestamp, prio)
936 else:
937 print "Unknown event", inhibit
938 sys.exit(1)
939
940 def idled(self, cpu, timestamp, td, pcomm, prio):
941 if (self.checkstamp(timestamp) == 0):
942 return
943 thread = self.findtd(td, pcomm)
944 Idle(thread, cpu, timestamp, prio)
945
946 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
947 if (self.checkstamp(timestamp) == 0):
948 return
949 thread = self.findtd(td, pcomm)
950 Preempted(thread, cpu, timestamp, prio,
951 self.findtd(bytd, bypcomm))
952
953 def switchin(self, cpu, timestamp, td, pcomm, prio):
954 if (self.checkstamp(timestamp) == 0):
955 return
956 thread = self.findtd(td, pcomm)
957 Running(thread, cpu, timestamp, prio)
958
959 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
960 if (self.checkstamp(timestamp) == 0):
961 return
962 thread = self.findtd(td, pcomm)
963 bythread = self.findtd(bytd, bypcomm)
964 Runq(thread, cpu, timestamp, prio, bythread)
965 Wokeup(bythread, cpu, timestamp, thread)
966
967 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
968 if (self.checkstamp(timestamp) == 0):
969 return
970 thread = self.findtd(td, pcomm)
971 KsegrpRunq(thread, cpu, timestamp, prio,
972 self.findtd(bytd, bypcomm))
973
974 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
975 if (self.checkstamp(timestamp) == 0):
976 return
977 thread = self.findtd(td, pcomm)
978 Sched_exit(thread, cpu, timestamp, prio)
979
980 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
981 if (self.checkstamp(timestamp) == 0):
982 return
983 self.stathz = stathz
984 cpu = int(cpu)
985 try:
986 ticks = self.ticks[cpu]
987 except:
988 self.ticks[cpu] = 0
989 self.ticks[cpu] += 1
990 thread = self.findtd(td, pcomm)
991 Tick(thread, cpu, timestamp, prio, stathz)
992
993 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
994 if (prio == newprio):
995 return
996 if (self.checkstamp(timestamp) == 0):
997 return
998 thread = self.findtd(td, pcomm)
999 bythread = self.findtd(bytd, bypcomm)
1000 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1001 Lend(bythread, cpu, timestamp, newprio, thread)
1002
1003 def cpuload(self, cpu, timestamp, count):
1004 if (self.checkstamp(timestamp) == 0):
1005 return
1006 cpu = int(cpu)
1007 try:
1008 load = self.load[cpu]
1009 except:
1010 load = Counter("cpu" + str(cpu) + " load")
1011 self.load[cpu] = load
1012 self.sources.insert(0, load)
1013 Count(load, cpu, timestamp, count)
1014
1015 def loadglobal(self, cpu, timestamp, count):
1016 if (self.checkstamp(timestamp) == 0):
1017 return
1018 cpu = 0
1019 try:
1020 load = self.load[cpu]
1021 except:
1022 load = Counter("CPU load")
1023 self.load[cpu] = load
1024 self.sources.insert(0, load)
1025 Count(load, cpu, timestamp, count)
1026
1027 def findtd(self, td, pcomm):
1028 for thread in self.threads:
1029 if (thread.str == td and thread.name == pcomm):
1030 return thread
1031 thread = Thread(td, pcomm)
1032 self.threads.append(thread)
1033 self.sources.append(thread)
1034 return (thread)
1035
1036 def fixup(self):
1037 for source in self.sources:
1038 Padevent(source, -1, self.timestamp_last)
1039 Padevent(source, -1, self.timestamp_first, last=1)
1040 source.fixup()
1041
1042class SchedDisplay(Canvas):
1043 def __init__(self, master):
1044 self.ratio = 1
1045 self.ktrfile = None
1046 self.sources = None
1047 self.bdheight = 10
1048 self.events = {}
1049
1050 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1051 scrollregion=(0, 0, 800, 500))
1052
1053 def setfile(self, ktrfile):
1054 self.ktrfile = ktrfile
1055 self.sources = ktrfile.sources
1056
1057 def draw(self):
1058 ypos = 0
1059 xsize = self.xsize()
1060 for source in self.sources:
1061 status.startup("Drawing " + source.name)
1062 self.create_line(0, ypos, xsize, ypos,
1063 width=1, fill="black", tags=("all",))
1064 ypos += self.bdheight
1065 ypos += source.ysize()
1066 source.draw(self, ypos)
1067 ypos += self.bdheight
1068 try:
1069 self.tag_raise("point", "state")
1070 self.tag_lower("cpuinfo", "all")
1071 except:
1072 pass
1073 self.create_line(0, ypos, xsize, ypos,
1074 width=1, fill="black", tags=("all",))
1075 self.tag_bind("event", "<Enter>", self.mouseenter)
1076 self.tag_bind("event", "<Leave>", self.mouseexit)
1077 self.tag_bind("event", "<Button-1>", self.mousepress)
1078
1079 def mouseenter(self, event):
1080 item, = self.find_withtag(CURRENT)
1081 event = self.events[item]
1082 event.mouseenter(self, item)
1083
1084 def mouseexit(self, event):
1085 item, = self.find_withtag(CURRENT)
1086 event = self.events[item]
1087 event.mouseexit(self, item)
1088
1089 def mousepress(self, event):
1090 item, = self.find_withtag(CURRENT)
1091 event = self.events[item]
1092 event.mousepress(self, item)
1093
1094 def drawnames(self, canvas):
1095 status.startup("Drawing names")
1096 ypos = 0
1097 canvas.configure(scrollregion=(0, 0,
1098 canvas["width"], self.ysize()))
1099 for source in self.sources:
1100 canvas.create_line(0, ypos, canvas["width"], ypos,
1101 width=1, fill="black", tags=("all",))
1102 ypos += self.bdheight
1103 ypos += source.ysize()
1104 source.drawname(canvas, ypos)
1105 ypos += self.bdheight
1106 canvas.create_line(0, ypos, canvas["width"], ypos,
1107 width=1, fill="black", tags=("all",))
1108
1109 def xsize(self):
1110 return ((self.ktrfile.timespan() / self.ratio) + 20)
1111
1112 def ysize(self):
1113 ysize = 0
1114 for source in self.sources:
1115 ysize += source.ysize() + (self.bdheight * 2)
1116 return (ysize)
1117
1118 def scaleset(self, ratio):
1119 if (self.ktrfile == None):
1120 return
1121 oldratio = self.ratio
1122 xstart, ystart = self.xview()
1123 length = (float(self["width"]) / self.xsize())
1124 middle = xstart + (length / 2)
1125
1126 self.ratio = ratio
1127 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1128 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1129
1130 length = (float(self["width"]) / self.xsize())
1131 xstart = middle - (length / 2)
1132 self.xview_moveto(xstart)
1133
1134 def scaleget(self):
1135 return self.ratio
1136
1137 def setcolor(self, tag, color):
1138 self.itemconfigure(tag, state="normal", fill=color)
1139
1140 def hide(self, tag):
1141 self.itemconfigure(tag, state="hidden")
1142
1143class GraphMenu(Frame):
1144 def __init__(self, master):
1145 Frame.__init__(self, master, bd=2, relief=RAISED)
1146 self.view = Menubutton(self, text="Configure")
1147 self.viewmenu = Menu(self.view, tearoff=0)
1148 self.viewmenu.add_command(label="Events",
1149 command=self.econf)
1150 self.view["menu"] = self.viewmenu
1151 self.view.pack(side=LEFT)
1152
1153 def econf(self):
1154 EventConfigure()
1155
1156
1157class SchedGraph(Frame):
1158 def __init__(self, master):
1159 Frame.__init__(self, master)
1160 self.menu = None
1161 self.names = None
1162 self.display = None
1163 self.scale = None
1164 self.status = None
1165 self.pack(expand=1, fill="both")
1166 self.buildwidgets()
1167 self.layout()
1168 self.draw(sys.argv[1])
1169
1170 def buildwidgets(self):
1171 global status
1172 self.menu = GraphMenu(self)
1173 self.display = SchedDisplay(self)
1174 self.names = Canvas(self,
1175 width=100, height=self.display["height"],
1176 bg='grey', scrollregion=(0, 0, 50, 100))
1177 self.scale = Scaler(self, self.display)
1178 status = self.status = Status(self)
1179 self.scrollY = Scrollbar(self, orient="vertical",
1180 command=self.display_yview)
1181 self.display.scrollX = Scrollbar(self, orient="horizontal",
1182 command=self.display.xview)
1183 self.display["xscrollcommand"] = self.display.scrollX.set
1184 self.display["yscrollcommand"] = self.scrollY.set
1185 self.names["yscrollcommand"] = self.scrollY.set
1186
1187 def layout(self):
1188 self.columnconfigure(1, weight=1)
1189 self.rowconfigure(1, weight=1)
1190 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1191 self.names.grid(row=1, column=0, sticky=N+S)
1192 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1193 self.scrollY.grid(row=1, column=2, sticky=N+S)
1194 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1195 sticky=E+W)
1196 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1197 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1198
1199 def draw(self, file):
1200 self.master.update()
1201 ktrfile = KTRFile(file)
1202 self.display.setfile(ktrfile)
1203 self.display.drawnames(self.names)
1204 self.display.draw()
1205 self.scale.set(250000)
1206 self.display.xview_moveto(0)
1207
1208 def display_yview(self, *args):
1209 self.names.yview(*args)
1210 self.display.yview(*args)
1211
1212 def setcolor(self, tag, color):
1213 self.display.setcolor(tag, color)
1214
1215 def hide(self, tag):
1216 self.display.hide(tag)
1217
1218if (len(sys.argv) != 2):
1219 print "usage:", sys.argv[0], "<ktr file>"
1220 sys.exit(1)
1221
1222root = Tk()
1223root.title("Scheduler Graph")
1224graph = SchedGraph(root)
1225root.mainloop()
744 l = canvas.create_rectangle(self.cpux,
745 ypos - self.ysize() - canvas.bdheight,
746 xpos, ypos + canvas.bdheight, fill=color, width=0,
747 tags=("all", "cpuinfo"))
748
749 def ysize(self):
750 return (None)
751
752 def eventat(self, i):
753 if (i >= len(self.events)):
754 return (None)
755 event = self.events[i]
756 return (event)
757
758 def findevent(self, timestamp):
759 for event in self.events:
760 if (event.timestamp >= timestamp and event.real):
761 return (event)
762 return (None)
763
764class Thread(EventSource):
765 names = {}
766 def __init__(self, td, pcomm):
767 EventSource.__init__(self, pcomm)
768 self.str = td
769 try:
770 cnt = Thread.names[pcomm]
771 except:
772 Thread.names[pcomm] = 0
773 return
774 Thread.names[pcomm] = cnt + 1
775
776 def fixup(self):
777 cnt = Thread.names[self.name]
778 if (cnt == 0):
779 return
780 cnt -= 1
781 Thread.names[self.name] = cnt
782 self.name += " td" + str(cnt)
783
784 def ysize(self):
785 return (10)
786
787class Counter(EventSource):
788 max = 0
789 def __init__(self, name):
790 EventSource.__init__(self, name)
791
792 def event(self, event):
793 EventSource.event(self, event)
794 try:
795 count = event.count
796 except:
797 return
798 count = int(count)
799 if (count > Counter.max):
800 Counter.max = count
801
802 def ysize(self):
803 return (80)
804
805 def yscale(self):
806 return (self.ysize() / Counter.max)
807
808
809class KTRFile:
810 def __init__(self, file):
811 self.timestamp_first = None
812 self.timestamp_last = None
813 self.lineno = -1
814 self.threads = []
815 self.sources = []
816 self.ticks = {}
817 self.load = {}
818
819 self.parse(file)
820 self.fixup()
821 global ticksps
822 ticksps = self.ticksps()
823
824 def parse(self, file):
825 try:
826 ifp = open(file)
827 except:
828 print "Can't open", file
829 sys.exit(1)
830
831 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+"
832 tdname = "(\S+)\(([^)]*)\)"
833
834 ktrstr = "mi_switch: " + tdname
835 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
836 switchout_re = re.compile(ktrhdr + ktrstr)
837
838 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
839 idled_re = re.compile(ktrhdr + ktrstr)
840
841 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
842 ktrstr += tdname
843 preempted_re = re.compile(ktrhdr + ktrstr)
844
845 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
846 switchin_re = re.compile(ktrhdr + ktrstr)
847
848 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
849 sched_add_re = re.compile(ktrhdr + ktrstr)
850
851 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
852 setrunqueue_re = re.compile(ktrhdr + ktrstr)
853
854 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
855 sched_rem_re = re.compile(ktrhdr + ktrstr)
856
857 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
858 sched_exit_re = re.compile(ktrhdr + ktrstr)
859
860 ktrstr = "statclock: " + tdname + " prio (\d+)"
861 ktrstr += " stathz (\d+)"
862 sched_clock_re = re.compile(ktrhdr + ktrstr)
863
864 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
865 ktrstr += " newprio (\d+) by " + tdname
866 sched_prio_re = re.compile(ktrhdr + ktrstr)
867
868 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
869 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
870
871 parsers = [[cpuload_re, self.cpuload],
872 [loadglobal_re, self.loadglobal],
873 [switchin_re, self.switchin],
874 [switchout_re, self.switchout],
875 [sched_add_re, self.sched_add],
876 [setrunqueue_re, self.sched_rem],
877 [sched_prio_re, self.sched_prio],
878 [preempted_re, self.preempted],
879 [sched_rem_re, self.sched_rem],
880 [sched_exit_re, self.sched_exit],
881 [sched_clock_re, self.sched_clock],
882 [idled_re, self.idled]]
883
884 for line in ifp.readlines():
885 self.lineno += 1
886 if ((self.lineno % 1024) == 0):
887 status.startup("Parsing line " +
888 str(self.lineno))
889 for p in parsers:
890 m = p[0].match(line)
891 if (m != None):
892 p[1](*m.groups())
893 break
894 # if (m == None):
895 # print line,
896
897 def checkstamp(self, timestamp):
898 timestamp = int(timestamp)
899 if (self.timestamp_first == None):
900 self.timestamp_first = timestamp
901 if (timestamp > self.timestamp_first):
902 print "Bad timestamp on line ", self.lineno
903 return (0)
904 self.timestamp_last = timestamp
905 return (1)
906
907 def timespan(self):
908 return (self.timestamp_first - self.timestamp_last);
909
910 def ticksps(self):
911 return (self.timespan() / self.ticks[0]) * int(self.stathz)
912
913 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
914 TDI_SUSPENDED = 0x0001
915 TDI_SLEEPING = 0x0002
916 TDI_SWAPPED = 0x0004
917 TDI_LOCK = 0x0008
918 TDI_IWAIT = 0x0010
919
920 if (self.checkstamp(timestamp) == 0):
921 return
922 inhibit = int(inhibit)
923 thread = self.findtd(td, pcomm)
924 if (inhibit & TDI_SWAPPED):
925 Swapped(thread, cpu, timestamp, prio)
926 elif (inhibit & TDI_SLEEPING):
927 Sleep(thread, cpu, timestamp, prio, wmesg)
928 elif (inhibit & TDI_LOCK):
929 Blocked(thread, cpu, timestamp, prio, lock)
930 elif (inhibit & TDI_IWAIT):
931 Iwait(thread, cpu, timestamp, prio)
932 elif (inhibit & TDI_SUSPENDED):
933 Suspended(thread, cpu, timestamp, prio)
934 elif (inhibit == 0):
935 Yielding(thread, cpu, timestamp, prio)
936 else:
937 print "Unknown event", inhibit
938 sys.exit(1)
939
940 def idled(self, cpu, timestamp, td, pcomm, prio):
941 if (self.checkstamp(timestamp) == 0):
942 return
943 thread = self.findtd(td, pcomm)
944 Idle(thread, cpu, timestamp, prio)
945
946 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
947 if (self.checkstamp(timestamp) == 0):
948 return
949 thread = self.findtd(td, pcomm)
950 Preempted(thread, cpu, timestamp, prio,
951 self.findtd(bytd, bypcomm))
952
953 def switchin(self, cpu, timestamp, td, pcomm, prio):
954 if (self.checkstamp(timestamp) == 0):
955 return
956 thread = self.findtd(td, pcomm)
957 Running(thread, cpu, timestamp, prio)
958
959 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
960 if (self.checkstamp(timestamp) == 0):
961 return
962 thread = self.findtd(td, pcomm)
963 bythread = self.findtd(bytd, bypcomm)
964 Runq(thread, cpu, timestamp, prio, bythread)
965 Wokeup(bythread, cpu, timestamp, thread)
966
967 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
968 if (self.checkstamp(timestamp) == 0):
969 return
970 thread = self.findtd(td, pcomm)
971 KsegrpRunq(thread, cpu, timestamp, prio,
972 self.findtd(bytd, bypcomm))
973
974 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
975 if (self.checkstamp(timestamp) == 0):
976 return
977 thread = self.findtd(td, pcomm)
978 Sched_exit(thread, cpu, timestamp, prio)
979
980 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
981 if (self.checkstamp(timestamp) == 0):
982 return
983 self.stathz = stathz
984 cpu = int(cpu)
985 try:
986 ticks = self.ticks[cpu]
987 except:
988 self.ticks[cpu] = 0
989 self.ticks[cpu] += 1
990 thread = self.findtd(td, pcomm)
991 Tick(thread, cpu, timestamp, prio, stathz)
992
993 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
994 if (prio == newprio):
995 return
996 if (self.checkstamp(timestamp) == 0):
997 return
998 thread = self.findtd(td, pcomm)
999 bythread = self.findtd(bytd, bypcomm)
1000 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1001 Lend(bythread, cpu, timestamp, newprio, thread)
1002
1003 def cpuload(self, cpu, timestamp, count):
1004 if (self.checkstamp(timestamp) == 0):
1005 return
1006 cpu = int(cpu)
1007 try:
1008 load = self.load[cpu]
1009 except:
1010 load = Counter("cpu" + str(cpu) + " load")
1011 self.load[cpu] = load
1012 self.sources.insert(0, load)
1013 Count(load, cpu, timestamp, count)
1014
1015 def loadglobal(self, cpu, timestamp, count):
1016 if (self.checkstamp(timestamp) == 0):
1017 return
1018 cpu = 0
1019 try:
1020 load = self.load[cpu]
1021 except:
1022 load = Counter("CPU load")
1023 self.load[cpu] = load
1024 self.sources.insert(0, load)
1025 Count(load, cpu, timestamp, count)
1026
1027 def findtd(self, td, pcomm):
1028 for thread in self.threads:
1029 if (thread.str == td and thread.name == pcomm):
1030 return thread
1031 thread = Thread(td, pcomm)
1032 self.threads.append(thread)
1033 self.sources.append(thread)
1034 return (thread)
1035
1036 def fixup(self):
1037 for source in self.sources:
1038 Padevent(source, -1, self.timestamp_last)
1039 Padevent(source, -1, self.timestamp_first, last=1)
1040 source.fixup()
1041
1042class SchedDisplay(Canvas):
1043 def __init__(self, master):
1044 self.ratio = 1
1045 self.ktrfile = None
1046 self.sources = None
1047 self.bdheight = 10
1048 self.events = {}
1049
1050 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1051 scrollregion=(0, 0, 800, 500))
1052
1053 def setfile(self, ktrfile):
1054 self.ktrfile = ktrfile
1055 self.sources = ktrfile.sources
1056
1057 def draw(self):
1058 ypos = 0
1059 xsize = self.xsize()
1060 for source in self.sources:
1061 status.startup("Drawing " + source.name)
1062 self.create_line(0, ypos, xsize, ypos,
1063 width=1, fill="black", tags=("all",))
1064 ypos += self.bdheight
1065 ypos += source.ysize()
1066 source.draw(self, ypos)
1067 ypos += self.bdheight
1068 try:
1069 self.tag_raise("point", "state")
1070 self.tag_lower("cpuinfo", "all")
1071 except:
1072 pass
1073 self.create_line(0, ypos, xsize, ypos,
1074 width=1, fill="black", tags=("all",))
1075 self.tag_bind("event", "<Enter>", self.mouseenter)
1076 self.tag_bind("event", "<Leave>", self.mouseexit)
1077 self.tag_bind("event", "<Button-1>", self.mousepress)
1078
1079 def mouseenter(self, event):
1080 item, = self.find_withtag(CURRENT)
1081 event = self.events[item]
1082 event.mouseenter(self, item)
1083
1084 def mouseexit(self, event):
1085 item, = self.find_withtag(CURRENT)
1086 event = self.events[item]
1087 event.mouseexit(self, item)
1088
1089 def mousepress(self, event):
1090 item, = self.find_withtag(CURRENT)
1091 event = self.events[item]
1092 event.mousepress(self, item)
1093
1094 def drawnames(self, canvas):
1095 status.startup("Drawing names")
1096 ypos = 0
1097 canvas.configure(scrollregion=(0, 0,
1098 canvas["width"], self.ysize()))
1099 for source in self.sources:
1100 canvas.create_line(0, ypos, canvas["width"], ypos,
1101 width=1, fill="black", tags=("all",))
1102 ypos += self.bdheight
1103 ypos += source.ysize()
1104 source.drawname(canvas, ypos)
1105 ypos += self.bdheight
1106 canvas.create_line(0, ypos, canvas["width"], ypos,
1107 width=1, fill="black", tags=("all",))
1108
1109 def xsize(self):
1110 return ((self.ktrfile.timespan() / self.ratio) + 20)
1111
1112 def ysize(self):
1113 ysize = 0
1114 for source in self.sources:
1115 ysize += source.ysize() + (self.bdheight * 2)
1116 return (ysize)
1117
1118 def scaleset(self, ratio):
1119 if (self.ktrfile == None):
1120 return
1121 oldratio = self.ratio
1122 xstart, ystart = self.xview()
1123 length = (float(self["width"]) / self.xsize())
1124 middle = xstart + (length / 2)
1125
1126 self.ratio = ratio
1127 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1128 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1129
1130 length = (float(self["width"]) / self.xsize())
1131 xstart = middle - (length / 2)
1132 self.xview_moveto(xstart)
1133
1134 def scaleget(self):
1135 return self.ratio
1136
1137 def setcolor(self, tag, color):
1138 self.itemconfigure(tag, state="normal", fill=color)
1139
1140 def hide(self, tag):
1141 self.itemconfigure(tag, state="hidden")
1142
1143class GraphMenu(Frame):
1144 def __init__(self, master):
1145 Frame.__init__(self, master, bd=2, relief=RAISED)
1146 self.view = Menubutton(self, text="Configure")
1147 self.viewmenu = Menu(self.view, tearoff=0)
1148 self.viewmenu.add_command(label="Events",
1149 command=self.econf)
1150 self.view["menu"] = self.viewmenu
1151 self.view.pack(side=LEFT)
1152
1153 def econf(self):
1154 EventConfigure()
1155
1156
1157class SchedGraph(Frame):
1158 def __init__(self, master):
1159 Frame.__init__(self, master)
1160 self.menu = None
1161 self.names = None
1162 self.display = None
1163 self.scale = None
1164 self.status = None
1165 self.pack(expand=1, fill="both")
1166 self.buildwidgets()
1167 self.layout()
1168 self.draw(sys.argv[1])
1169
1170 def buildwidgets(self):
1171 global status
1172 self.menu = GraphMenu(self)
1173 self.display = SchedDisplay(self)
1174 self.names = Canvas(self,
1175 width=100, height=self.display["height"],
1176 bg='grey', scrollregion=(0, 0, 50, 100))
1177 self.scale = Scaler(self, self.display)
1178 status = self.status = Status(self)
1179 self.scrollY = Scrollbar(self, orient="vertical",
1180 command=self.display_yview)
1181 self.display.scrollX = Scrollbar(self, orient="horizontal",
1182 command=self.display.xview)
1183 self.display["xscrollcommand"] = self.display.scrollX.set
1184 self.display["yscrollcommand"] = self.scrollY.set
1185 self.names["yscrollcommand"] = self.scrollY.set
1186
1187 def layout(self):
1188 self.columnconfigure(1, weight=1)
1189 self.rowconfigure(1, weight=1)
1190 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1191 self.names.grid(row=1, column=0, sticky=N+S)
1192 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1193 self.scrollY.grid(row=1, column=2, sticky=N+S)
1194 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1195 sticky=E+W)
1196 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1197 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1198
1199 def draw(self, file):
1200 self.master.update()
1201 ktrfile = KTRFile(file)
1202 self.display.setfile(ktrfile)
1203 self.display.drawnames(self.names)
1204 self.display.draw()
1205 self.scale.set(250000)
1206 self.display.xview_moveto(0)
1207
1208 def display_yview(self, *args):
1209 self.names.yview(*args)
1210 self.display.yview(*args)
1211
1212 def setcolor(self, tag, color):
1213 self.display.setcolor(tag, color)
1214
1215 def hide(self, tag):
1216 self.display.hide(tag)
1217
1218if (len(sys.argv) != 2):
1219 print "usage:", sys.argv[0], "<ktr file>"
1220 sys.exit(1)
1221
1222root = Tk()
1223root.title("Scheduler Graph")
1224graph = SchedGraph(root)
1225root.mainloop()