Deleted Added
sdiff udiff text old ( 187156 ) new ( 187358 )
full compact
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:

--- 10 unchanged lines hidden (view full) ---

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 187156 2009-01-13 16:44:18Z jhb $
28
29import sys
30import re
31from Tkinter import *
32
33# To use:
34# - Install the ports/x11-toolkits/py-tkinter package; e.g.
35# portinstall x11-toolkits/py-tkinter package
36# - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF; e.g.
37# options KTR
38# options KTR_ENTRIES=32768

--- 9 unchanged lines hidden (view full) ---

48# - While the workload is continuing (i.e. before it finishes), disable
49# KTR tracing by setting 'sysctl debug.ktr.mask=0'. This is necessary
50# to avoid a race condition while running ktrdump, i.e. the KTR ring buffer
51# will cycle a bit while ktrdump runs, and this confuses schedgraph because
52# the timestamps appear to go backwards at some point. Stopping KTR logging
53# while the workload is still running is to avoid wasting log entries on
54# "idle" time at the end.
55# - Dump the trace to a file: 'ktrdump -ct > ktr.out'
56# - Run the python script: 'python schedgraph.py ktr.out'
57#
58# To do:
59# 1) Add a per-thread summary display
60# 2) Add bounding box style zoom.
61# 3) Click to center.
62# 4) Implement some sorting mechanism.
63# 5) Widget to display variable-range data (e.g. q length)
64# 6) Reorder rows, hide rows, etc.
65# 7) "Vertical rule" to help relate data in different rows
66# 8) Mouse-over popup of full thread/event/row lable (currently truncated)
67# 9) More visible anchors for popup event windows
68#
69# BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of
70# colours to represent them ;-)
71# 2) Extremely short traces may cause a crash because the code
72# assumes there is always at least one stathz entry logged, and
73# the number of such events is used as a denominator
74
75ticksps = None
76status = None
77configtypes = []
78lineno = -1
79
80def ticks2sec(ticks):
81 us = ticksps / 1000000
82 ticks /= us
83 if (ticks < 1000):
84 return (str(ticks) + "us")
85 ticks /= 1000
86 if (ticks < 1000):
87 return (str(ticks) + "ms")

--- 31 unchanged lines hidden (view full) ---

119
120 def clear(self):
121 self.label.config(text="")
122
123 def startup(self, str):
124 self.set(str)
125 root.update()
126
127class EventConf(Frame):
128 def __init__(self, master, name, color, enabled):
129 Frame.__init__(self, master)
130 self.name = name
131 self.color = StringVar()
132 self.color_default = color
133 self.color_current = color
134 self.color.set(color)
135 self.enabled = IntVar()
136 self.enabled_default = enabled
137 self.enabled_current = enabled
138 self.enabled.set(enabled)
139 self.draw()
140
141 def draw(self):
142 self.label = Label(self, text=self.name, anchor=W)
143 self.sample = Canvas(self, width=24, height=24,
144 bg='grey')
145 self.rect = self.sample.create_rectangle(0, 0, 24, 24,
146 fill=self.color.get())
147 self.list = OptionMenu(self, self.color,
148 "dark red", "red", "pink",
149 "dark orange", "orange",
150 "yellow", "light yellow",
151 "dark green", "green", "light green",
152 "dark blue", "blue", "light blue",
153 "dark violet", "violet", "purple",
154 "dark grey", "light grey",
155 "white", "black",
156 command=self.setcolor)
157 self.checkbox = Checkbutton(self, text="enabled",
158 variable=self.enabled)
159 self.label.grid(row=0, column=0, sticky=E+W)
160 self.sample.grid(row=0, column=1)
161 self.list.grid(row=0, column=2, sticky=E+W)
162 self.checkbox.grid(row=0, column=3)
163 self.columnconfigure(0, weight=1)
164 self.columnconfigure(2, minsize=110)
165
166 def setcolor(self, color):
167 self.color.set(color)
168 self.sample.itemconfigure(self.rect, fill=color)
169
170 def apply(self):
171 cchange = 0
172 echange = 0

--- 8 unchanged lines hidden (view full) ---

181 graph.setcolor(self.name, self.color_current)
182 else:
183 graph.hide(self.name)
184 return
185 if (cchange != 0):
186 graph.setcolor(self.name, self.color_current)
187
188 def revert(self):
189 self.setcolor(self.color_current)
190 self.enabled.set(self.enabled_current)
191
192 def default(self):
193 self.setcolor(self.color_default)
194 self.enabled.set(self.enabled_default)
195
196class EventConfigure(Toplevel):
197 def __init__(self):
198 Toplevel.__init__(self)
199 self.resizable(0, 0)
200 self.title("Event Configuration")
201 self.items = LabelFrame(self, text="Event Type")
202 self.buttons = Frame(self)
203 self.drawbuttons()
204 self.items.grid(row=0, column=0, sticky=E+W)
205 self.columnconfigure(0, weight=1)
206 self.buttons.grid(row=1, column=0, sticky=E+W)
207 self.types = []
208 self.irow = 0
209 for type in configtypes:
210 self.additem(type.name, type.color, type.enabled)
211
212 def additem(self, name, color, enabled=1):
213 item = EventConf(self.items, name, color, enabled)
214 self.types.append(item)
215 item.grid(row=self.irow, column=0, sticky=E+W)
216 self.irow += 1
217
218 def drawbuttons(self):
219 self.apply = Button(self.buttons, text="Apply",
220 command=self.apress)
221 self.revert = Button(self.buttons, text="Revert",
222 command=self.rpress)
223 self.default = Button(self.buttons, text="Default",
224 command=self.dpress)
225 self.apply.grid(row=0, column=0, sticky=E+W)
226 self.revert.grid(row=0, column=1, sticky=E+W)
227 self.default.grid(row=0, column=2, sticky=E+W)
228 self.buttons.columnconfigure(0, weight=1)
229 self.buttons.columnconfigure(1, weight=1)
230 self.buttons.columnconfigure(2, weight=1)
231
232 def apress(self):
233 for item in self.types:
234 item.apply()
235
236 def rpress(self):
237 for item in self.types:
238 item.revert()
239
240 def dpress(self):
241 for item in self.types:
242 item.default()
243
244class EventView(Toplevel):
245 def __init__(self, event, canvas):
246 Toplevel.__init__(self)
247 self.resizable(0, 0)
248 self.title("Event")
249 self.event = event
250 self.frame = Frame(self)
251 self.frame.grid(row=0, column=0, sticky=N+S+E+W)
252 self.buttons = Frame(self)
253 self.buttons.grid(row=1, column=0, sticky=E+W)
254 self.canvas = canvas
255 self.drawlabels()
256 self.drawbuttons()
257 event.displayref(canvas)
258 self.bind("<Destroy>", self.destroycb)
259
260 def destroycb(self, event):
261 self.unbind("<Destroy>")

--- 5 unchanged lines hidden (view full) ---

267 def clearlabels(self):
268 for label in self.frame.grid_slaves():
269 label.grid_remove()
270
271 def drawlabels(self):
272 ypos = 0
273 labels = self.event.labels()
274 while (len(labels) < 7):
275 labels.append(("", "", 0))
276 for label in labels:
277 name, value, linked = label
278 l = Label(self.frame, text=name, bd=1, width=15,
279 relief=SUNKEN, anchor=W)
280 if (linked):
281 fgcolor = "blue"
282 else:
283 fgcolor = "black"
284 r = Label(self.frame, text=value, bd=1,
285 relief=SUNKEN, anchor=W, fg=fgcolor)

--- 22 unchanged lines hidden (view full) ---

308
309 def npress(self):
310 EventView(self.event, self.canvas)
311
312 def bpress(self):
313 prev = self.event.prev()
314 if (prev == None):
315 return
316 while (prev.real == 0):
317 prev = prev.prev()
318 if (prev == None):
319 return
320 self.newevent(prev)
321
322 def fpress(self):
323 next = self.event.next()
324 if (next == None):
325 return
326 while (next.real == 0):
327 next = next.next()
328 if (next == None):
329 return
330 self.newevent(next)
331
332 def linkpress(self, wevent):
333 event = self.event.getlinked()
334 if (event != None):
335 self.newevent(event)
336
337class Event:
338 name = "none"
339 color = "grey"
340 def __init__(self, source, cpu, timestamp, last=0):
341 self.source = source
342 self.cpu = cpu
343 self.timestamp = int(timestamp)
344 self.entries = []
345 self.real = 1
346 self.idx = None
347 self.state = 0
348 self.item = None
349 self.dispcnt = 0
350 self.linked = None
351 self.recno = lineno
352 if (last):
353 source.lastevent(self)
354 else:
355 source.event(self)
356
357 def status(self):
358 statstr = self.name + " " + self.source.name
359 statstr += " on: cpu" + str(self.cpu)
360 statstr += " at: " + str(self.timestamp)
361 statstr += self.stattxt()
362 status.set(statstr)
363
364 def stattxt(self):
365 return ""
366
367 def textadd(self, tuple):
368 pass
369 self.entries.append(tuple)
370
371 def labels(self):
372 return [("Source:", self.source.name, 0),
373 ("Event:", self.name, 0),
374 ("CPU:", self.cpu, 0),
375 ("Timestamp:", self.timestamp, 0),
376 ("Record: ", self.recno, 0)
377 ] + self.entries
378 def mouseenter(self, canvas, item):
379 self.displayref(canvas)
380 self.status()
381
382 def mouseexit(self, canvas, item):
383 self.displayunref(canvas)
384 status.clear()
385
386 def mousepress(self, canvas, item):
387 EventView(self, canvas)
388
389 def next(self):
390 return self.source.eventat(self.idx + 1)
391
392 def prev(self):
393 return self.source.eventat(self.idx - 1)
394
395 def displayref(self, canvas):
396 if (self.dispcnt == 0):
397 canvas.itemconfigure(self.item, width=2)
398 self.dispcnt += 1
399
400 def displayunref(self, canvas):
401 self.dispcnt -= 1
402 if (self.dispcnt == 0):
403 canvas.itemconfigure(self.item, width=0)
404 canvas.tag_raise("point", "state")
405
406 def getlinked(self):
407 return self.linked.findevent(self.timestamp)
408
409class PointEvent(Event):
410 def __init__(self, thread, cpu, timestamp, last=0):
411 Event.__init__(self, thread, cpu, timestamp, last)
412
413 def draw(self, canvas, xpos, ypos):
414 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
415 fill=self.color, tags=("all", "point", "event")
416 + (self.name,), width=0)
417 canvas.events[l] = self
418 self.item = l
419 if (self.enabled == 0):
420 canvas.itemconfigure(l, state="hidden")
421
422 return (xpos)
423
424class StateEvent(Event):
425 def __init__(self, thread, cpu, timestamp, last=0):
426 Event.__init__(self, thread, cpu, timestamp, last)
427 self.duration = 0
428 self.skipnext = 0
429 self.skipself = 0
430 self.state = 1
431
432 def draw(self, canvas, xpos, ypos):
433 next = self.nextstate()
434 if (self.skipself == 1 or next == None):
435 return (xpos)
436 while (self.skipnext):
437 skipped = next
438 next.skipself = 1
439 next.real = 0
440 next = next.nextstate()
441 if (next == None):
442 next = skipped
443 self.skipnext -= 1
444 self.duration = next.timestamp - self.timestamp
445 if (self.duration < 0):
446 self.duration = 0
447 print "Unsynchronized timestamp"
448 print self.cpu, self.timestamp
449 print next.cpu, next.timestamp
450 delta = self.duration / canvas.ratio
451 l = canvas.create_rectangle(xpos, ypos,
452 xpos + delta, ypos - 10, fill=self.color, width=0,
453 tags=("all", "state", "event") + (self.name,))
454 canvas.events[l] = self
455 self.item = l
456 if (self.enabled == 0):
457 canvas.itemconfigure(l, state="hidden")
458
459 return (xpos + delta)
460
461 def stattxt(self):
462 return " duration: " + ticks2sec(self.duration)
463
464 def nextstate(self):
465 next = self.next()
466 while (next != None and next.state == 0):
467 next = next.next()
468 return (next)
469
470 def labels(self):
471 return [("Source:", self.source.name, 0),
472 ("Event:", self.name, 0),
473 ("Timestamp:", self.timestamp, 0),
474 ("CPU:", self.cpu, 0),
475 ("Record:", self.recno, 0),
476 ("Duration:", ticks2sec(self.duration), 0)
477 ] + self.entries
478
479class Count(Event):
480 name = "Count"
481 color = "red"
482 enabled = 1
483 def __init__(self, source, cpu, timestamp, count):
484 self.count = int(count)
485 Event.__init__(self, source, cpu, timestamp)
486 self.duration = 0
487 self.textadd(("count:", self.count, 0))
488
489 def draw(self, canvas, xpos, ypos):
490 next = self.next()
491 self.duration = next.timestamp - self.timestamp
492 delta = self.duration / canvas.ratio
493 yhight = self.source.yscale() * self.count
494 l = canvas.create_rectangle(xpos, ypos - yhight,
495 xpos + delta, ypos, fill=self.color, width=0,
496 tags=("all", "count", "event") + (self.name,))
497 canvas.events[l] = self
498 self.item = l
499 if (self.enabled == 0):
500 canvas.itemconfigure(l, state="hidden")
501 return (xpos + delta)
502
503 def stattxt(self):
504 return " count: " + str(self.count)
505
506configtypes.append(Count)
507
508class Running(StateEvent):
509 name = "running"
510 color = "green"
511 enabled = 1
512 def __init__(self, thread, cpu, timestamp, prio):
513 StateEvent.__init__(self, thread, cpu, timestamp)
514 self.prio = prio
515 self.textadd(("prio:", self.prio, 0))
516
517configtypes.append(Running)
518
519class Idle(StateEvent):
520 name = "idle"
521 color = "grey"
522 enabled = 0
523 def __init__(self, thread, cpu, timestamp, prio):
524 StateEvent.__init__(self, thread, cpu, timestamp)
525 self.prio = prio
526 self.textadd(("prio:", self.prio, 0))
527
528configtypes.append(Idle)
529
530class Yielding(StateEvent):
531 name = "yielding"
532 color = "yellow"
533 enabled = 1
534 def __init__(self, thread, cpu, timestamp, prio):
535 StateEvent.__init__(self, thread, cpu, timestamp)
536 self.skipnext = 0
537 self.prio = prio
538 self.textadd(("prio:", self.prio, 0))
539
540configtypes.append(Yielding)
541
542class Swapped(StateEvent):
543 name = "swapped"
544 color = "violet"
545 enabled = 1
546 def __init__(self, thread, cpu, timestamp, prio):
547 StateEvent.__init__(self, thread, cpu, timestamp)
548 self.prio = prio
549 self.textadd(("prio:", self.prio, 0))
550
551configtypes.append(Swapped)
552
553class Suspended(StateEvent):
554 name = "suspended"
555 color = "purple"
556 enabled = 1
557 def __init__(self, thread, cpu, timestamp, prio):
558 StateEvent.__init__(self, thread, cpu, timestamp)
559 self.prio = prio
560 self.textadd(("prio:", self.prio, 0))
561
562configtypes.append(Suspended)
563
564class Iwait(StateEvent):
565 name = "iwait"
566 color = "grey"
567 enabled = 0
568 def __init__(self, thread, cpu, timestamp, prio):
569 StateEvent.__init__(self, thread, cpu, timestamp)
570 self.prio = prio
571 self.textadd(("prio:", self.prio, 0))
572
573configtypes.append(Iwait)
574
575class Preempted(StateEvent):
576 name = "preempted"
577 color = "red"
578 enabled = 1
579 def __init__(self, thread, cpu, timestamp, prio, bythread):
580 StateEvent.__init__(self, thread, cpu, timestamp)
581 self.skipnext = 1
582 self.prio = prio
583 self.linked = bythread
584 self.textadd(("prio:", self.prio, 0))
585 self.textadd(("by thread:", self.linked.name, 1))
586
587configtypes.append(Preempted)
588
589class Sleep(StateEvent):
590 name = "sleep"
591 color = "blue"
592 enabled = 1
593 def __init__(self, thread, cpu, timestamp, prio, wmesg):
594 StateEvent.__init__(self, thread, cpu, timestamp)
595 self.prio = prio
596 self.wmesg = wmesg
597 self.textadd(("prio:", self.prio, 0))
598 self.textadd(("wmesg:", self.wmesg, 0))
599
600 def stattxt(self):
601 statstr = StateEvent.stattxt(self)
602 statstr += " sleeping on: " + self.wmesg
603 return (statstr)
604
605configtypes.append(Sleep)
606
607class Blocked(StateEvent):
608 name = "blocked"
609 color = "dark red"
610 enabled = 1
611 def __init__(self, thread, cpu, timestamp, prio, lock):
612 StateEvent.__init__(self, thread, cpu, timestamp)
613 self.prio = prio
614 self.lock = lock
615 self.textadd(("prio:", self.prio, 0))
616 self.textadd(("lock:", self.lock, 0))
617
618 def stattxt(self):
619 statstr = StateEvent.stattxt(self)
620 statstr += " blocked on: " + self.lock
621 return (statstr)
622
623configtypes.append(Blocked)
624
625class KsegrpRunq(StateEvent):
626 name = "KsegrpRunq"
627 color = "orange"
628 enabled = 1
629 def __init__(self, thread, cpu, timestamp, prio, bythread):
630 StateEvent.__init__(self, thread, cpu, timestamp)
631 self.prio = prio
632 self.linked = bythread
633 self.textadd(("prio:", self.prio, 0))
634 self.textadd(("by thread:", self.linked.name, 1))
635
636configtypes.append(KsegrpRunq)
637
638class Runq(StateEvent):
639 name = "Runq"
640 color = "yellow"
641 enabled = 1
642 def __init__(self, thread, cpu, timestamp, prio, bythread):
643 StateEvent.__init__(self, thread, cpu, timestamp)
644 self.prio = prio
645 self.linked = bythread
646 self.textadd(("prio:", self.prio, 0))
647 self.textadd(("by thread:", self.linked.name, 1))
648
649configtypes.append(Runq)
650
651class Sched_exit_thread(StateEvent):
652 name = "exit_thread"
653 color = "grey"
654 enabled = 0
655 def __init__(self, thread, cpu, timestamp, prio):
656 StateEvent.__init__(self, thread, cpu, timestamp)
657 self.name = "sched_exit_thread"
658 self.prio = prio
659 self.textadd(("prio:", self.prio, 0))
660
661configtypes.append(Sched_exit_thread)
662
663class Sched_exit(StateEvent):
664 name = "exit"
665 color = "grey"
666 enabled = 0
667 def __init__(self, thread, cpu, timestamp, prio):
668 StateEvent.__init__(self, thread, cpu, timestamp)
669 self.name = "sched_exit"
670 self.prio = prio
671 self.textadd(("prio:", self.prio, 0))
672
673configtypes.append(Sched_exit)
674
675# Events for running callout routines
676
677class CalloutIdle(StateEvent):
678 name = "callwheel idle"
679 color = "grey"
680 enabled = 0
681 def __init__(self, wheel, cpu, timestamp):
682 StateEvent.__init__(self, wheel, cpu, timestamp)
683
684configtypes.append(CalloutIdle)
685
686class CalloutRunning(StateEvent):
687 name = "callout running"
688 color = "green"
689 enabled = 1
690 def __init__(self, wheel, cpu, timestamp, func, arg):
691 StateEvent.__init__(self, wheel, cpu, timestamp)
692 self.textadd(("function:", func, 0))
693 self.textadd(("argument:", arg, 0))
694 self.arg = arg
695 self.func = func
696
697 def stattxt(self):
698 statstr = StateEvent.stattxt(self)
699 statstr += " executing %s(%s)" % (self.func, self.arg)
700 return (statstr)
701
702configtypes.append(CalloutRunning)
703
704# Events on locks
705#
706# XXX: No support for upgrade/downgrade currently or differentiating
707# between read/write in general.
708#
709# XXX: Point events for recursion perhaps?
710
711class LockAcquire(StateEvent):
712 name = "lock acquire"
713 color = "blue"
714 enabled = 1
715 def __init__(self, lock, cpu, timestamp, file, line):
716 StateEvent.__init__(self, lock, cpu, timestamp)
717 self.textadd(("file:", file, 0))
718 self.textadd(("line:", line, 0))
719
720configtypes.append(LockAcquire)
721
722class LockContest(StateEvent):
723 name = "lock contest"
724 color = "purple"
725 enabled = 1
726 def __init__(self, lock, cpu, timestamp, file, line):
727 StateEvent.__init__(self, lock, cpu, timestamp)
728 self.textadd(("file:", file, 0))
729 self.textadd(("line:", line, 0))
730
731configtypes.append(LockContest)
732
733class LockFailedTry(PointEvent):
734 name = "failed lock try"
735 color = "red"
736 enabled = 1
737 def __init__(self, lock, cpu, timestamp, file, line):
738 PointEvent.__init__(self, lock, cpu, timestamp)
739 self.textadd(("file:", file, 0))
740 self.textadd(("line:", line, 0))
741
742configtypes.append(LockFailedTry)
743
744class LockRelease(StateEvent):
745 name = "lock release"
746 color = "grey"
747 enabled = 0
748 def __init__(self, lock, cpu, timestamp, file, line):
749 StateEvent.__init__(self, lock, cpu, timestamp)
750 self.textadd(("file:", file, 0))
751 self.textadd(("line:", line, 0))
752
753configtypes.append(LockRelease)
754
755class Padevent(StateEvent):
756 def __init__(self, thread, cpu, timestamp, last=0):
757 StateEvent.__init__(self, thread, cpu, timestamp, last)
758 self.name = "pad"
759 self.real = 0
760
761 def draw(self, canvas, xpos, ypos):
762 next = self.next()
763 if (next == None):
764 return (xpos)
765 self.duration = next.timestamp - self.timestamp
766 delta = self.duration / canvas.ratio
767 return (xpos + delta)
768
769class Tick(PointEvent):
770 name = "tick"
771 color = "black"
772 enabled = 0
773 def __init__(self, thread, cpu, timestamp, prio, stathz):
774 PointEvent.__init__(self, thread, cpu, timestamp)
775 self.prio = prio
776 self.textadd(("prio:", self.prio, 0))
777
778configtypes.append(Tick)
779
780class Prio(PointEvent):
781 name = "prio"
782 color = "black"
783 enabled = 0
784 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
785 PointEvent.__init__(self, thread, cpu, timestamp)
786 self.prio = prio
787 self.newprio = newprio
788 self.linked = bythread
789 self.textadd(("new prio:", self.newprio, 0))
790 self.textadd(("prio:", self.prio, 0))
791 if (self.linked != self.source):
792 self.textadd(("by thread:", self.linked.name, 1))
793 else:
794 self.textadd(("by thread:", self.linked.name, 0))
795
796configtypes.append(Prio)
797
798class Lend(PointEvent):
799 name = "lend"
800 color = "black"
801 enabled = 0
802 def __init__(self, thread, cpu, timestamp, prio, tothread):
803 PointEvent.__init__(self, thread, cpu, timestamp)
804 self.prio = prio
805 self.linked = tothread
806 self.textadd(("prio:", self.prio, 0))
807 self.textadd(("to thread:", self.linked.name, 1))
808
809configtypes.append(Lend)
810
811class Wokeup(PointEvent):
812 name = "wokeup"
813 color = "black"
814 enabled = 0
815 def __init__(self, thread, cpu, timestamp, ranthread):
816 PointEvent.__init__(self, thread, cpu, timestamp)
817 self.linked = ranthread
818 self.textadd(("ran thread:", self.linked.name, 1))
819
820configtypes.append(Wokeup)
821
822(DEFAULT, LOAD, COUNT, CALLWHEEL, LOCK, THREAD) = range(6)
823
824class EventSource:
825 def __init__(self, name, group=DEFAULT, order=0):
826 self.name = name
827 self.events = []
828 self.cpu = 0
829 self.cpux = 0
830 self.group = group
831 self.order = order
832
833 def __cmp__(self, other):
834 if (self.group == other.group):
835 return cmp(self.order, other.order)
836 return cmp(self.group, other.group)
837
838 # It is much faster to append items to a list then to insert them
839 # at the beginning. As a result, we add events in reverse order
840 # and then swap the list during fixup.
841 def fixup(self):
842 self.events.reverse()
843
844 def event(self, event):
845 self.events.append(event)
846
847 def remove(self, event):
848 self.events.remove(event)
849
850 def lastevent(self, event):
851 self.events.insert(0, event)
852
853 def draw(self, canvas, ypos):
854 xpos = 10
855 self.cpux = 10
856 self.cpu = self.events[1].cpu
857 for i in range(0, len(self.events)):
858 self.events[i].idx = i
859 for event in self.events:
860 if (event.cpu != self.cpu and event.cpu != -1):
861 self.drawcpu(canvas, xpos, ypos)
862 self.cpux = xpos
863 self.cpu = event.cpu
864 xpos = event.draw(canvas, xpos, ypos)
865 self.drawcpu(canvas, xpos, ypos)
866
867 def drawname(self, canvas, ypos):
868 ypos = ypos - (self.ysize() / 2)
869 canvas.create_text(10, ypos, anchor="w", text=self.name)
870
871 def drawcpu(self, canvas, xpos, ypos):
872 cpu = int(self.cpu)
873 if (cpu == 0):
874 color = 'light grey'
875 elif (cpu == 1):
876 color = 'dark grey'
877 elif (cpu == 2):
878 color = 'light blue'
879 elif (cpu == 3):
880 color = 'light green'
881 elif (cpu == 4):
882 color = 'blanched almond'
883 elif (cpu == 5):
884 color = 'slate grey'
885 elif (cpu == 6):
886 color = 'light slate blue'
887 elif (cpu == 7):
888 color = 'thistle'
889 else:
890 color = "white"
891 l = canvas.create_rectangle(self.cpux,
892 ypos - self.ysize() - canvas.bdheight,
893 xpos, ypos + canvas.bdheight, fill=color, width=0,
894 tags=("all", "cpuinfo"))
895
896 def ysize(self):
897 return (None)
898
899 def eventat(self, i):
900 if (i >= len(self.events)):
901 return (None)
902 event = self.events[i]
903 return (event)
904
905 def findevent(self, timestamp):
906 for event in self.events:
907 if (event.timestamp >= timestamp and event.real):
908 return (event)
909 return (None)
910
911class Thread(EventSource):
912 names = {}
913 def __init__(self, td, pcomm):
914 EventSource.__init__(self, pcomm, THREAD)
915 self.str = td
916 try:
917 cnt = Thread.names[pcomm]
918 except:
919 Thread.names[pcomm] = 0
920 return
921 Thread.names[pcomm] = cnt + 1
922
923 def fixup(self):
924 EventSource.fixup(self)
925 cnt = Thread.names[self.name]
926 if (cnt == 0):
927 return
928 cnt -= 1
929 Thread.names[self.name] = cnt
930 self.name += " td" + str(cnt)
931
932 def ysize(self):
933 return (10)
934
935class Callwheel(EventSource):
936 count = 0
937 def __init__(self, cpu):
938 EventSource.__init__(self, "Callwheel", CALLWHEEL, cpu)
939 self.wheel = cpu
940 Callwheel.count += 1
941
942 def fixup(self):
943 EventSource.fixup(self)
944 if (Callwheel.count == 1):
945 return
946 self.name += " (CPU %d)" % (self.wheel)
947
948 def ysize(self):
949 return (10)
950
951class Lock(EventSource):
952 def __init__(self, lock):
953 EventSource.__init__(self, lock, LOCK)
954
955 def ysize(self):
956 return (10)
957
958class Counter(EventSource):
959 max = 0
960 def __init__(self, name):
961 EventSource.__init__(self, name, COUNT)
962
963 def event(self, event):
964 EventSource.event(self, event)
965 try:
966 count = event.count
967 except:
968 return
969 count = int(count)
970 if (count > Counter.max):
971 Counter.max = count
972
973 def ymax(self):
974 return (Counter.max)
975
976 def ysize(self):
977 return (80)
978
979 def yscale(self):
980 return (self.ysize() / Counter.max)
981
982class CPULoad(Counter):
983 def __init__(self, cpu):
984 Counter.__init__(self, "cpu" + str(cpu) + " load")
985 self.group = LOAD
986 self.order = cpu
987
988class KTRFile:
989 def __init__(self, file):
990 self.timestamp_f = None
991 self.timestamp_l = None
992 self.threads = []
993 self.sources = []
994 self.locks = {}
995 self.callwheels = {}
996 self.ticks = {}
997 self.load = {}
998 self.crit = {}
999 self.stathz = 0
1000
1001 self.parse(file)
1002 self.fixup()
1003 global ticksps
1004 print "first", self.timestamp_f, "last", self.timestamp_l
1005 print "time span", self.timespan()
1006 print "stathz", self.stathz
1007 ticksps = self.ticksps()
1008 print "Ticks per second", ticksps
1009
1010 def parse(self, file):
1011 try:
1012 ifp = open(file)
1013 except:
1014 print "Can't open", file
1015 sys.exit(1)
1016
1017 ktrhdr = "\s*\d+\s+(\d+)\s+(\d+)\s+"
1018 tdname = "(\S+)\(([^)]*)\)"
1019 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
1020
1021# XXX doesn't handle:
1022# 371 0 61628682318 mi_switch: 0xc075c070(swapper) prio 180 inhibit 2 wmesg ATA request done lock (null)
1023 ktrstr = "mi_switch: " + tdname
1024 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
1025 switchout_re = re.compile(ktrhdr + ktrstr)
1026
1027 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
1028 idled_re = re.compile(ktrhdr + ktrstr)
1029
1030 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
1031 ktrstr += tdname
1032 preempted_re = re.compile(ktrhdr + ktrstr)
1033
1034 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
1035 switchin_re = re.compile(ktrhdr + ktrstr)
1036
1037 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
1038 sched_add_re = re.compile(ktrhdr + ktrstr)
1039
1040 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
1041 setrunqueue_re = re.compile(ktrhdr + ktrstr)
1042
1043 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
1044 sched_rem_re = re.compile(ktrhdr + ktrstr)
1045
1046 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
1047 sched_exit_thread_re = re.compile(ktrhdr + ktrstr)
1048
1049 ktrstr = "sched_exit: " + tdname + " prio (\d+)"
1050 sched_exit_re = re.compile(ktrhdr + ktrstr)
1051
1052 ktrstr = "statclock: " + tdname + " prio (\d+)"
1053 ktrstr += " stathz (\d+)"
1054 sched_clock_re = re.compile(ktrhdr + ktrstr)
1055
1056 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
1057 ktrstr += " newprio (\d+) by " + tdname
1058 sched_prio_re = re.compile(ktrhdr + ktrstr)
1059
1060 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
1061 cpuload2_re = re.compile(ktrhdr + "cpu (\d+) load: (\d+)")
1062 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
1063
1064 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
1065 critsec_re = re.compile(ktrhdr + ktrstr)
1066
1067 ktrstr = "callout 0x[a-f\d]+ "
1068 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1069 callout_start_re = re.compile(ktrhdr + ktrstr)
1070
1071 ktrstr = "callout mpsafe 0x[a-f\d]+ "
1072 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1073 callout_mpsafe_re = re.compile(ktrhdr + ktrstr)
1074
1075 ktrstr = "callout mtx 0x[a-f\d]+ "
1076 ktrstr += "func (0x[a-f\d]+) arg (0x[a-f\d]+)"
1077 callout_mtx_re = re.compile(ktrhdr + ktrstr)
1078
1079 ktrstr = "callout 0x[a-f\d]+ finished"
1080 callout_stop_re = re.compile(ktrhdr + ktrstr)
1081
1082 ktrstr = "TRY_([RSWX]?LOCK) \(.*\) (.*) r = ([0-9]+)"
1083 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1084 lock_try_re = re.compile(ktrhdr + ktrstr)
1085
1086 ktrstr = "([RSWX]?UNLOCK) \(.*\) (.*) r = ([0-9]+)"
1087 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1088 lock_release_re = re.compile(ktrhdr + ktrstr)
1089
1090 ktrstr = "([RSWX]?LOCK) \(.*\) (.*) r = ([0-9]+)"
1091 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1092 lock_acquire_re = re.compile(ktrhdr + ktrstr)
1093
1094 ktrstr = "_mtx_lock_sleep: (.*) contested \(lock=0x?[0-9a-f]*\)"
1095 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1096 mtx_contested_re = re.compile(ktrhdr + ktrstr)
1097
1098 # XXX: Spin lock traces don't have lock name or file/line
1099
1100 ktrstr = "_rw_wlock_hard: (.*) contested \(lock=0x?[0-9a-f]*\)"
1101 ktrstr += " at (?:\.\./)*(.*):([0-9]+)"
1102 rw_contested_re = re.compile(ktrhdr + ktrstr)
1103
1104 # XXX: Read lock traces for rwlocks contesting don't have
1105 # lock name or file/line
1106
1107 parsers = [[cpuload_re, self.cpuload],
1108 [cpuload2_re, self.cpuload2],
1109 [loadglobal_re, self.loadglobal],
1110 [switchin_re, self.switchin],
1111 [switchout_re, self.switchout],
1112 [sched_add_re, self.sched_add],
1113 [setrunqueue_re, self.sched_rem],
1114 [sched_prio_re, self.sched_prio],
1115 [preempted_re, self.preempted],
1116 [sched_rem_re, self.sched_rem],
1117 [sched_exit_thread_re, self.sched_exit_thread],
1118 [sched_exit_re, self.sched_exit],
1119 [sched_clock_re, self.sched_clock],
1120 [critsec_re, self.critsec],
1121 [callout_start_re, self.callout_start],
1122 [callout_mpsafe_re, self.callout_start],
1123 [callout_mtx_re, self.callout_start],
1124 [callout_stop_re, self.callout_stop],
1125 [lock_try_re, self.lock_try],
1126 [lock_release_re, self.lock_release],
1127 [lock_acquire_re, self.lock_acquire],
1128 [mtx_contested_re, self.lock_contest],
1129 [rw_contested_re, self.lock_contest],
1130 [idled_re, self.idled]]
1131
1132 global lineno
1133 lineno = 0
1134 for line in ifp.readlines():
1135 lineno += 1
1136 if ((lineno % 1024) == 0):
1137 status.startup("Parsing line " + str(lineno))
1138 for p in parsers:
1139 m = p[0].match(line)
1140 if (m != None):
1141 p[1](*m.groups())
1142 break
1143 if (m == None):
1144 print line,
1145
1146 def checkstamp(self, cpu, timestamp):
1147 timestamp = int(timestamp)
1148 if (self.timestamp_f == None):
1149 self.timestamp_f = timestamp;
1150 if (self.timestamp_l != None and timestamp > self.timestamp_l):
1151 return (0)
1152 self.timestamp_l = timestamp;
1153 return (timestamp)
1154
1155 def timespan(self):
1156 return (self.timestamp_f - self.timestamp_l);
1157
1158 def ticksps(self):
1159 return (self.timespan() / self.ticks[0]) * int(self.stathz)
1160
1161 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
1162 TDI_SUSPENDED = 0x0001
1163 TDI_SLEEPING = 0x0002
1164 TDI_SWAPPED = 0x0004
1165 TDI_LOCK = 0x0008
1166 TDI_IWAIT = 0x0010
1167
1168 timestamp = self.checkstamp(cpu, timestamp)
1169 if (timestamp == 0):
1170 return
1171 inhibit = int(inhibit)
1172 thread = self.findtd(td, pcomm)
1173 if (inhibit & TDI_SWAPPED):
1174 Swapped(thread, cpu, timestamp, prio)
1175 elif (inhibit & TDI_SLEEPING):
1176 Sleep(thread, cpu, timestamp, prio, wmesg)
1177 elif (inhibit & TDI_LOCK):
1178 Blocked(thread, cpu, timestamp, prio, lock)
1179 elif (inhibit & TDI_IWAIT):
1180 Iwait(thread, cpu, timestamp, prio)
1181 elif (inhibit & TDI_SUSPENDED):
1182 Suspended(thread, cpu, timestamp, prio)
1183 elif (inhibit == 0):
1184 Yielding(thread, cpu, timestamp, prio)
1185 else:
1186 print "Unknown event", inhibit
1187 sys.exit(1)
1188
1189 def idled(self, cpu, timestamp, td, pcomm, prio):
1190 timestamp = self.checkstamp(cpu, timestamp)
1191 if (timestamp == 0):
1192 return
1193 thread = self.findtd(td, pcomm)
1194 Idle(thread, cpu, timestamp, prio)
1195
1196 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1197 timestamp = self.checkstamp(cpu, timestamp)
1198 if (timestamp == 0):
1199 return
1200 thread = self.findtd(td, pcomm)
1201 Preempted(thread, cpu, timestamp, prio,
1202 self.findtd(bytd, bypcomm))
1203
1204 def switchin(self, cpu, timestamp, td, pcomm, prio):
1205 timestamp = self.checkstamp(cpu, timestamp)
1206 if (timestamp == 0):
1207 return
1208 thread = self.findtd(td, pcomm)
1209 Running(thread, cpu, timestamp, prio)
1210
1211 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1212 timestamp = self.checkstamp(cpu, timestamp)
1213 if (timestamp == 0):
1214 return
1215 thread = self.findtd(td, pcomm)
1216 bythread = self.findtd(bytd, bypcomm)
1217 Runq(thread, cpu, timestamp, prio, bythread)
1218 Wokeup(bythread, cpu, timestamp, thread)
1219
1220 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
1221 timestamp = self.checkstamp(cpu, timestamp)
1222 if (timestamp == 0):
1223 return
1224 thread = self.findtd(td, pcomm)
1225 KsegrpRunq(thread, cpu, timestamp, prio,
1226 self.findtd(bytd, bypcomm))
1227
1228 def sched_exit_thread(self, cpu, timestamp, td, pcomm, prio):
1229 timestamp = self.checkstamp(cpu, timestamp)
1230 if (timestamp == 0):
1231 return
1232 thread = self.findtd(td, pcomm)
1233 Sched_exit_thread(thread, cpu, timestamp, prio)
1234
1235 def sched_exit(self, cpu, timestamp, td, pcomm, prio):
1236 timestamp = self.checkstamp(cpu, timestamp)
1237 if (timestamp == 0):
1238 return
1239 thread = self.findtd(td, pcomm)
1240 Sched_exit(thread, cpu, timestamp, prio)
1241
1242 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
1243 timestamp = self.checkstamp(cpu, timestamp)
1244 if (timestamp == 0):
1245 return
1246 self.stathz = stathz
1247 cpu = int(cpu)
1248 try:
1249 ticks = self.ticks[cpu]
1250 except:
1251 self.ticks[cpu] = 0
1252 self.ticks[cpu] += 1
1253 thread = self.findtd(td, pcomm)
1254 Tick(thread, cpu, timestamp, prio, stathz)
1255
1256 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1257 if (prio == newprio):
1258 return
1259 timestamp = self.checkstamp(cpu, timestamp)
1260 if (timestamp == 0):
1261 return
1262 thread = self.findtd(td, pcomm)
1263 bythread = self.findtd(bytd, bypcomm)
1264 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1265 Lend(bythread, cpu, timestamp, newprio, thread)
1266
1267 def cpuload(self, cpu, timestamp, count):
1268 timestamp = self.checkstamp(cpu, timestamp)
1269 if (timestamp == 0):
1270 return
1271 cpu = int(cpu)
1272 try:
1273 load = self.load[cpu]
1274 except:
1275 load = CPULoad(cpu)
1276 self.load[cpu] = load
1277 self.sources.append(load)
1278 Count(load, cpu, timestamp, count)
1279
1280 def cpuload2(self, cpu, timestamp, ncpu, count):
1281 timestamp = self.checkstamp(cpu, timestamp)
1282 if (timestamp == 0):
1283 return
1284 cpu = int(ncpu)
1285 try:
1286 load = self.load[cpu]
1287 except:
1288 load = CPULoad(cpu)
1289 self.load[cpu] = load
1290 self.sources.append(load)
1291 Count(load, cpu, timestamp, count)
1292
1293 def loadglobal(self, cpu, timestamp, count):
1294 timestamp = self.checkstamp(cpu, timestamp)
1295 if (timestamp == 0):
1296 return
1297 cpu = 0
1298 try:
1299 load = self.load[cpu]
1300 except:
1301 load = Counter("CPU load")
1302 self.load[cpu] = load
1303 self.sources.append(load)
1304 Count(load, cpu, timestamp, count)
1305
1306 def critsec(self, cpu, timestamp, td, pcomm, to):
1307 timestamp = self.checkstamp(cpu, timestamp)
1308 if (timestamp == 0):
1309 return
1310 cpu = int(cpu)
1311 try:
1312 crit = self.crit[cpu]
1313 except:
1314 crit = Counter("Critical Section")
1315 self.crit[cpu] = crit
1316 self.sources.append(crit)
1317 Count(crit, cpu, timestamp, to)
1318
1319 def callout_start(self, cpu, timestamp, func, arg):
1320 timestamp = self.checkstamp(cpu, timestamp)
1321 if (timestamp == 0):
1322 return
1323 wheel = self.findwheel(cpu)
1324 CalloutRunning(wheel, cpu, timestamp, func, arg)
1325
1326 def callout_stop(self, cpu, timestamp):
1327 timestamp = self.checkstamp(cpu, timestamp)
1328 if (timestamp == 0):
1329 return
1330 wheel = self.findwheel(cpu)
1331 CalloutIdle(wheel, cpu, timestamp)
1332
1333 def lock_try(self, cpu, timestamp, op, name, result, file, line):
1334 timestamp = self.checkstamp(cpu, timestamp)
1335 if (timestamp == 0):
1336 return
1337 lock = self.findlock(name)
1338 if (int(result) == 0):
1339 LockFailedTry(lock, cpu, timestamp, file, line)
1340 else:
1341 LockAcquire(lock, cpu, timestamp, file, line)
1342
1343 def lock_acquire(self, cpu, timestamp, op, name, recurse, file, line):
1344 if (int(recurse) != 0):
1345 return
1346 timestamp = self.checkstamp(cpu, timestamp)
1347 if (timestamp == 0):
1348 return
1349 lock = self.findlock(name)
1350 LockAcquire(lock, cpu, timestamp, file, line)
1351
1352 def lock_release(self, cpu, timestamp, op, name, recurse, file, line):
1353 if (int(recurse) != 0):
1354 return
1355 timestamp = self.checkstamp(cpu, timestamp)
1356 if (timestamp == 0):
1357 return
1358 lock = self.findlock(name)
1359 LockRelease(lock, cpu, timestamp, file, line)
1360
1361 def lock_contest(self, cpu, timestamp, name, file, line):
1362 timestamp = self.checkstamp(cpu, timestamp)
1363 if (timestamp == 0):
1364 return
1365 lock = self.findlock(name)
1366 LockContest(lock, cpu, timestamp, file, line)
1367
1368 def findlock(self, name):
1369 try:
1370 lock = self.locks[name]
1371 except:
1372 lock = Lock(name)
1373 self.locks[name] = lock
1374 self.sources.append(lock)
1375 return (lock)
1376
1377 def findwheel(self, cpu):
1378 cpu = int(cpu)
1379 try:
1380 wheel = self.callwheels[cpu]
1381 except:
1382 wheel = Callwheel(cpu)
1383 self.callwheels[cpu] = wheel
1384 self.sources.append(wheel)
1385 return (wheel)
1386
1387 def findtd(self, td, pcomm):
1388 for thread in self.threads:
1389 if (thread.str == td and thread.name == pcomm):
1390 return thread
1391 thread = Thread(td, pcomm)
1392 self.threads.append(thread)
1393 self.sources.append(thread)
1394 return (thread)
1395
1396 def fixup(self):
1397 for source in self.sources:
1398 Padevent(source, -1, self.timestamp_l)
1399 Padevent(source, -1, self.timestamp_f, last=1)
1400 source.fixup()
1401 self.sources.sort()
1402
1403class SchedDisplay(Canvas):
1404 def __init__(self, master):
1405 self.ratio = 1
1406 self.ktrfile = None
1407 self.sources = None
1408 self.parent = master
1409 self.bdheight = 10
1410 self.events = {}
1411
1412 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1413 scrollregion=(0, 0, 800, 500))
1414
1415 def setfile(self, ktrfile):
1416 self.ktrfile = ktrfile
1417 self.sources = ktrfile.sources
1418
1419 # Compute a ratio to ensure that the file's timespan fits into
1420 # 2^31. Although python may handle larger values for X
1421 # values, the Tk internals do not.
1422 self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1
1423
1424 def draw(self):
1425 ypos = 0
1426 xsize = self.xsize()
1427 for source in self.sources:
1428 status.startup("Drawing " + source.name)
1429 self.create_line(0, ypos, xsize, ypos,
1430 width=1, fill="black", tags=("all",))
1431 ypos += self.bdheight
1432 ypos += source.ysize()
1433 source.draw(self, ypos)
1434 ypos += self.bdheight
1435 try:
1436 self.tag_raise("point", "state")
1437 self.tag_lower("cpuinfo", "all")
1438 except:
1439 pass
1440 self.create_line(0, ypos, xsize, ypos,
1441 width=1, fill="black", tags=("all",))
1442 self.tag_bind("event", "<Enter>", self.mouseenter)
1443 self.tag_bind("event", "<Leave>", self.mouseexit)
1444 self.tag_bind("event", "<Button-1>", self.mousepress)
1445 self.bind("<Button-4>", self.wheelup)
1446 self.bind("<Button-5>", self.wheeldown)
1447
1448 def mouseenter(self, event):
1449 item, = self.find_withtag(CURRENT)
1450 event = self.events[item]
1451 event.mouseenter(self, item)
1452
1453 def mouseexit(self, event):
1454 item, = self.find_withtag(CURRENT)
1455 event = self.events[item]
1456 event.mouseexit(self, item)
1457
1458 def mousepress(self, event):
1459 item, = self.find_withtag(CURRENT)
1460 event = self.events[item]
1461 event.mousepress(self, item)
1462
1463 def wheeldown(self, event):
1464 self.parent.display_yview("scroll", 1, "units")
1465
1466 def wheelup(self, event):
1467 self.parent.display_yview("scroll", -1, "units")
1468
1469 def drawnames(self, canvas):
1470 status.startup("Drawing names")
1471 ypos = 0
1472 canvas.configure(scrollregion=(0, 0,
1473 canvas["width"], self.ysize()))
1474 for source in self.sources:
1475 canvas.create_line(0, ypos, canvas["width"], ypos,
1476 width=1, fill="black", tags=("all",))
1477 ypos += self.bdheight
1478 ypos += source.ysize()
1479 source.drawname(canvas, ypos)
1480 ypos += self.bdheight
1481 canvas.create_line(0, ypos, canvas["width"], ypos,
1482 width=1, fill="black", tags=("all",))
1483
1484 def xsize(self):
1485 return ((self.ktrfile.timespan() / self.ratio) + 20)
1486
1487 def ysize(self):
1488 ysize = 0
1489 for source in self.sources:
1490 ysize += source.ysize() + (self.bdheight * 2)
1491 return (ysize)
1492
1493 def scaleset(self, ratio):
1494 if (self.ktrfile == None):
1495 return
1496 oldratio = self.ratio
1497 xstart, ystart = self.xview()
1498 length = (float(self["width"]) / self.xsize())
1499 middle = xstart + (length / 2)
1500
1501 self.ratio = ratio
1502 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1503 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1504
1505 length = (float(self["width"]) / self.xsize())
1506 xstart = middle - (length / 2)
1507 self.xview_moveto(xstart)
1508
1509 def scaleget(self):
1510 return self.ratio
1511
1512 def setcolor(self, tag, color):
1513 self.itemconfigure(tag, state="normal", fill=color)
1514
1515 def hide(self, tag):
1516 self.itemconfigure(tag, state="hidden")
1517
1518class GraphMenu(Frame):
1519 def __init__(self, master):
1520 Frame.__init__(self, master, bd=2, relief=RAISED)
1521 self.view = Menubutton(self, text="Configure")
1522 self.viewmenu = Menu(self.view, tearoff=0)
1523 self.viewmenu.add_command(label="Events",
1524 command=self.econf)
1525 self.view["menu"] = self.viewmenu
1526 self.view.pack(side=LEFT)
1527
1528 def econf(self):
1529 EventConfigure()
1530
1531
1532class SchedGraph(Frame):
1533 def __init__(self, master):
1534 Frame.__init__(self, master)
1535 self.menu = None
1536 self.names = None
1537 self.display = None
1538 self.scale = None
1539 self.status = None
1540 self.pack(expand=1, fill="both")
1541 self.buildwidgets()
1542 self.layout()
1543 self.draw(sys.argv[1])
1544
1545 def buildwidgets(self):
1546 global status
1547 self.menu = GraphMenu(self)
1548 self.display = SchedDisplay(self)
1549 self.names = Canvas(self,
1550 width=120, height=self.display["height"],
1551 bg='grey', scrollregion=(0, 0, 50, 100))
1552 self.scale = Scaler(self, self.display)
1553 status = self.status = Status(self)
1554 self.scrollY = Scrollbar(self, orient="vertical",
1555 command=self.display_yview)
1556 self.display.scrollX = Scrollbar(self, orient="horizontal",
1557 command=self.display.xview)
1558 self.display["xscrollcommand"] = self.display.scrollX.set
1559 self.display["yscrollcommand"] = self.scrollY.set

--- 6 unchanged lines hidden (view full) ---

1566 self.names.grid(row=1, column=0, sticky=N+S)
1567 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1568 self.scrollY.grid(row=1, column=2, sticky=N+S)
1569 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1570 sticky=E+W)
1571 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1572 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1573
1574 def draw(self, file):
1575 self.master.update()
1576 ktrfile = KTRFile(file)
1577 self.display.setfile(ktrfile)
1578 self.display.drawnames(self.names)
1579 self.display.draw()
1580 self.scale.set(250000)
1581 self.display.xview_moveto(0)
1582
1583 def display_yview(self, *args):
1584 self.names.yview(*args)
1585 self.display.yview(*args)
1586
1587 def setcolor(self, tag, color):
1588 self.display.setcolor(tag, color)
1589
1590 def hide(self, tag):
1591 self.display.hide(tag)
1592
1593if (len(sys.argv) != 2):
1594 print "usage:", sys.argv[0], "<ktr file>"
1595 sys.exit(1)
1596
1597root = Tk()
1598root.title("Scheduler Graph")
1599graph = SchedGraph(root)
1600root.mainloop()