]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/sched/schedgraph.py
Copy xen includes in to RELENG_6 branch
[FreeBSD/FreeBSD.git] / tools / sched / schedgraph.py
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$
28
29 import sys
30 import re
31 from 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
48 ticksps = None
49 status = None
50 configtypes = []
51
52 def 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
66 class 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
84 class 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
101 class 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
170 class 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
218 class 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
311 class 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
380 class 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
395 class 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
444 class 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
471 configtypes.append(Count)
472
473 class 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
482 configtypes.append(Running)
483
484 class 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
493 configtypes.append(Idle)
494
495 class 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
505 configtypes.append(Yielding)
506
507 class 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
516 configtypes.append(Swapped)
517
518 class 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
527 configtypes.append(Suspended)
528
529 class 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
538 configtypes.append(Iwait)
539
540 class 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
552 configtypes.append(Preempted)
553
554 class 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
570 configtypes.append(Sleep)
571
572 class 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
588 configtypes.append(Blocked)
589
590 class 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
601 configtypes.append(KsegrpRunq)
602
603 class 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
614 configtypes.append(Runq)
615
616 class 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
626 configtypes.append(Sched_exit)
627
628 class 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
642 class 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
651 configtypes.append(Tick)
652
653 class 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
669 configtypes.append(Prio)
670
671 class 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
682 configtypes.append(Lend)
683
684 class 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
693 configtypes.append(Wokeup)
694
695 class 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'
742                 else:
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
764 class 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
787 class 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
809 class 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
1042 class 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
1143 class 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
1157 class 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
1218 if (len(sys.argv) != 2):
1219         print "usage:", sys.argv[0], "<ktr file>"
1220         sys.exit(1)
1221
1222 root = Tk()
1223 root.title("Scheduler Graph")
1224 graph = SchedGraph(root)
1225 root.mainloop()