]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/sched/schedgraph.py
This commit was generated by cvs2svn to compensate for changes in r162735,
[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                 self.crit = {}
819
820                 self.parse(file)
821                 self.fixup()
822                 global ticksps
823                 ticksps = self.ticksps()
824
825         def parse(self, file):
826                 try:
827                         ifp = open(file)
828                 except:
829                         print "Can't open", file
830                         sys.exit(1)
831
832                 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+"
833                 tdname = "(\S+)\(([^)]*)\)"
834                 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)"
835
836                 ktrstr = "mi_switch: " + tdname
837                 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
838                 switchout_re = re.compile(ktrhdr + ktrstr)
839
840                 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
841                 idled_re = re.compile(ktrhdr + ktrstr)
842
843                 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
844                 ktrstr += tdname
845                 preempted_re = re.compile(ktrhdr + ktrstr)
846
847                 ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
848                 switchin_re = re.compile(ktrhdr + ktrstr)
849
850                 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
851                 sched_add_re = re.compile(ktrhdr + ktrstr)
852
853                 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
854                 setrunqueue_re = re.compile(ktrhdr + ktrstr)
855
856                 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
857                 sched_rem_re = re.compile(ktrhdr + ktrstr)
858
859                 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
860                 sched_exit_re = re.compile(ktrhdr + ktrstr)
861
862                 ktrstr = "statclock: " + tdname + " prio (\d+)"
863                 ktrstr += " stathz (\d+)"
864                 sched_clock_re = re.compile(ktrhdr + ktrstr)
865
866                 ktrstr = "sched_prio: " + tdname + " prio (\d+)"
867                 ktrstr += " newprio (\d+) by " + tdname
868                 sched_prio_re = re.compile(ktrhdr + ktrstr)
869
870                 cpuload_re = re.compile(ktrhdr + "load: (\d+)")
871                 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)")
872
873                 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)"
874                 critsec_re = re.compile(ktrhdr + ktrstr)
875
876                 parsers = [[cpuload_re, self.cpuload],
877                            [loadglobal_re, self.loadglobal],
878                            [switchin_re, self.switchin],
879                            [switchout_re, self.switchout],
880                            [sched_add_re, self.sched_add],
881                            [setrunqueue_re, self.sched_rem],
882                            [sched_prio_re, self.sched_prio],
883                            [preempted_re, self.preempted],
884                            [sched_rem_re, self.sched_rem],
885                            [sched_exit_re, self.sched_exit],
886                            [sched_clock_re, self.sched_clock],
887                            [critsec_re, self.critsec],
888                            [idled_re, self.idled]]
889
890                 for line in ifp.readlines():
891                         self.lineno += 1
892                         if ((self.lineno % 1024) == 0):
893                                 status.startup("Parsing line " +
894                                     str(self.lineno))
895                         for p in parsers:
896                                 m = p[0].match(line)
897                                 if (m != None):
898                                         p[1](*m.groups())
899                                         break
900                         # if (m == None):
901                         #       print line,
902
903         def checkstamp(self, timestamp):
904                 timestamp = int(timestamp)
905                 if (self.timestamp_first == None):
906                         self.timestamp_first = timestamp
907                 if (timestamp > self.timestamp_first):
908                         print "Bad timestamp on line ", self.lineno
909                         return (0)
910                 self.timestamp_last = timestamp
911                 return (1)
912
913         def timespan(self):
914                 return (self.timestamp_first - self.timestamp_last);
915
916         def ticksps(self):
917                 return (self.timespan() / self.ticks[0]) * int(self.stathz)
918
919         def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
920                 TDI_SUSPENDED = 0x0001
921                 TDI_SLEEPING = 0x0002
922                 TDI_SWAPPED = 0x0004
923                 TDI_LOCK = 0x0008
924                 TDI_IWAIT = 0x0010 
925
926                 if (self.checkstamp(timestamp) == 0):
927                         return
928                 inhibit = int(inhibit)
929                 thread = self.findtd(td, pcomm)
930                 if (inhibit & TDI_SWAPPED):
931                         Swapped(thread, cpu, timestamp, prio)
932                 elif (inhibit & TDI_SLEEPING):
933                         Sleep(thread, cpu, timestamp, prio, wmesg)
934                 elif (inhibit & TDI_LOCK):
935                         Blocked(thread, cpu, timestamp, prio, lock)
936                 elif (inhibit & TDI_IWAIT):
937                         Iwait(thread, cpu, timestamp, prio)
938                 elif (inhibit & TDI_SUSPENDED):
939                         Suspended(thread, cpu, timestamp, prio)
940                 elif (inhibit == 0):
941                         Yielding(thread, cpu, timestamp, prio)
942                 else:
943                         print "Unknown event", inhibit
944                         sys.exit(1)
945                 
946         def idled(self, cpu, timestamp, td, pcomm, prio):
947                 if (self.checkstamp(timestamp) == 0):
948                         return
949                 thread = self.findtd(td, pcomm)
950                 Idle(thread, cpu, timestamp, prio)
951
952         def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
953                 if (self.checkstamp(timestamp) == 0):
954                         return
955                 thread = self.findtd(td, pcomm)
956                 Preempted(thread, cpu, timestamp, prio,
957                     self.findtd(bytd, bypcomm))
958
959         def switchin(self, cpu, timestamp, td, pcomm, prio):
960                 if (self.checkstamp(timestamp) == 0):
961                         return
962                 thread = self.findtd(td, pcomm)
963                 Running(thread, cpu, timestamp, prio)
964
965         def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
966                 if (self.checkstamp(timestamp) == 0):
967                         return
968                 thread = self.findtd(td, pcomm)
969                 bythread = self.findtd(bytd, bypcomm)
970                 Runq(thread, cpu, timestamp, prio, bythread)
971                 Wokeup(bythread, cpu, timestamp, thread)
972
973         def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
974                 if (self.checkstamp(timestamp) == 0):
975                         return
976                 thread = self.findtd(td, pcomm)
977                 KsegrpRunq(thread, cpu, timestamp, prio,
978                     self.findtd(bytd, bypcomm))
979
980         def sched_exit(self, cpu, timestamp, td, pcomm, prio):
981                 if (self.checkstamp(timestamp) == 0):
982                         return
983                 thread = self.findtd(td, pcomm)
984                 Sched_exit(thread, cpu, timestamp, prio)
985
986         def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
987                 if (self.checkstamp(timestamp) == 0):
988                         return
989                 self.stathz = stathz
990                 cpu = int(cpu)
991                 try:
992                         ticks = self.ticks[cpu]
993                 except:
994                         self.ticks[cpu] = 0
995                 self.ticks[cpu] += 1
996                 thread = self.findtd(td, pcomm)
997                 Tick(thread, cpu, timestamp, prio, stathz)
998
999         def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
1000                 if (prio == newprio):
1001                         return
1002                 if (self.checkstamp(timestamp) == 0):
1003                         return
1004                 thread = self.findtd(td, pcomm)
1005                 bythread = self.findtd(bytd, bypcomm)
1006                 Prio(thread, cpu, timestamp, prio, newprio, bythread)
1007                 Lend(bythread, cpu, timestamp, newprio, thread)
1008
1009         def cpuload(self, cpu, timestamp, count):
1010                 if (self.checkstamp(timestamp) == 0):
1011                         return
1012                 cpu = int(cpu)
1013                 try:
1014                         load = self.load[cpu]
1015                 except:
1016                         load = Counter("cpu" + str(cpu) + " load")
1017                         self.load[cpu] = load
1018                         self.sources.insert(0, load)
1019                 Count(load, cpu, timestamp, count)
1020
1021         def loadglobal(self, cpu, timestamp, count):
1022                 if (self.checkstamp(timestamp) == 0):
1023                         return
1024                 cpu = 0
1025                 try:
1026                         load = self.load[cpu]
1027                 except:
1028                         load = Counter("CPU load")
1029                         self.load[cpu] = load
1030                         self.sources.insert(0, load)
1031                 Count(load, cpu, timestamp, count)
1032
1033         def critsec(self, cpu, timestamp, td, pcomm, to):
1034                 if (self.checkstamp(timestamp) == 0):
1035                         return
1036                 cpu = int(cpu)
1037                 try:
1038                         crit = self.crit[cpu]
1039                 except:
1040                         crit = Counter("Critical Section")
1041                         self.crit[cpu] = crit
1042                         self.sources.insert(0, crit)
1043                 Count(crit, cpu, timestamp, to)
1044
1045         def findtd(self, td, pcomm):
1046                 for thread in self.threads:
1047                         if (thread.str == td and thread.name == pcomm):
1048                                 return thread
1049                 thread = Thread(td, pcomm)
1050                 self.threads.append(thread)
1051                 self.sources.append(thread)
1052                 return (thread)
1053
1054         def fixup(self):
1055                 for source in self.sources:
1056                         Padevent(source, -1, self.timestamp_last)
1057                         Padevent(source, -1, self.timestamp_first, last=1)
1058                         source.fixup()
1059
1060 class SchedDisplay(Canvas):
1061         def __init__(self, master):
1062                 self.ratio = 1
1063                 self.ktrfile = None
1064                 self.sources = None
1065                 self.bdheight = 10 
1066                 self.events = {}
1067
1068                 Canvas.__init__(self, master, width=800, height=500, bg='grey',
1069                      scrollregion=(0, 0, 800, 500))
1070
1071         def setfile(self, ktrfile):
1072                 self.ktrfile = ktrfile
1073                 self.sources = ktrfile.sources
1074
1075         def draw(self):
1076                 ypos = 0
1077                 xsize = self.xsize()
1078                 for source in self.sources:
1079                         status.startup("Drawing " + source.name)
1080                         self.create_line(0, ypos, xsize, ypos,
1081                             width=1, fill="black", tags=("all",))
1082                         ypos += self.bdheight
1083                         ypos += source.ysize()
1084                         source.draw(self, ypos)
1085                         ypos += self.bdheight
1086                         try:
1087                                 self.tag_raise("point", "state")
1088                                 self.tag_lower("cpuinfo", "all")
1089                         except:
1090                                 pass
1091                 self.create_line(0, ypos, xsize, ypos,
1092                     width=1, fill="black", tags=("all",))
1093                 self.tag_bind("event", "<Enter>", self.mouseenter)
1094                 self.tag_bind("event", "<Leave>", self.mouseexit)
1095                 self.tag_bind("event", "<Button-1>", self.mousepress)
1096
1097         def mouseenter(self, event):
1098                 item, = self.find_withtag(CURRENT)
1099                 event = self.events[item]
1100                 event.mouseenter(self, item)
1101
1102         def mouseexit(self, event):
1103                 item, = self.find_withtag(CURRENT)
1104                 event = self.events[item]
1105                 event.mouseexit(self, item)
1106
1107         def mousepress(self, event):
1108                 item, = self.find_withtag(CURRENT)
1109                 event = self.events[item]
1110                 event.mousepress(self, item)
1111
1112         def drawnames(self, canvas):
1113                 status.startup("Drawing names")
1114                 ypos = 0
1115                 canvas.configure(scrollregion=(0, 0,
1116                     canvas["width"], self.ysize()))
1117                 for source in self.sources:
1118                         canvas.create_line(0, ypos, canvas["width"], ypos,
1119                             width=1, fill="black", tags=("all",))
1120                         ypos += self.bdheight
1121                         ypos += source.ysize()
1122                         source.drawname(canvas, ypos)
1123                         ypos += self.bdheight
1124                 canvas.create_line(0, ypos, canvas["width"], ypos,
1125                     width=1, fill="black", tags=("all",))
1126
1127         def xsize(self):
1128                 return ((self.ktrfile.timespan() / self.ratio) + 20)
1129
1130         def ysize(self):
1131                 ysize = 0
1132                 for source in self.sources:
1133                         ysize += source.ysize() + (self.bdheight * 2)
1134                 return (ysize)
1135
1136         def scaleset(self, ratio):
1137                 if (self.ktrfile == None):
1138                         return
1139                 oldratio = self.ratio
1140                 xstart, ystart = self.xview()
1141                 length = (float(self["width"]) / self.xsize())
1142                 middle = xstart + (length / 2)
1143
1144                 self.ratio = ratio
1145                 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1146                 self.scale("all", 0, 0, float(oldratio) / ratio, 1)
1147
1148                 length = (float(self["width"]) / self.xsize())
1149                 xstart = middle - (length / 2)
1150                 self.xview_moveto(xstart)
1151
1152         def scaleget(self):
1153                 return self.ratio
1154
1155         def setcolor(self, tag, color):
1156                 self.itemconfigure(tag, state="normal", fill=color)
1157
1158         def hide(self, tag):
1159                 self.itemconfigure(tag, state="hidden")
1160
1161 class GraphMenu(Frame):
1162         def __init__(self, master):
1163                 Frame.__init__(self, master, bd=2, relief=RAISED)
1164                 self.view = Menubutton(self, text="Configure")
1165                 self.viewmenu = Menu(self.view, tearoff=0)
1166                 self.viewmenu.add_command(label="Events",
1167                     command=self.econf)
1168                 self.view["menu"] = self.viewmenu
1169                 self.view.pack(side=LEFT)
1170
1171         def econf(self):
1172                 EventConfigure()
1173
1174
1175 class SchedGraph(Frame):
1176         def __init__(self, master):
1177                 Frame.__init__(self, master)
1178                 self.menu = None
1179                 self.names = None
1180                 self.display = None
1181                 self.scale = None
1182                 self.status = None
1183                 self.pack(expand=1, fill="both")
1184                 self.buildwidgets()
1185                 self.layout()
1186                 self.draw(sys.argv[1])
1187
1188         def buildwidgets(self):
1189                 global status
1190                 self.menu = GraphMenu(self)
1191                 self.display = SchedDisplay(self)
1192                 self.names = Canvas(self,
1193                     width=100, height=self.display["height"],
1194                     bg='grey', scrollregion=(0, 0, 50, 100))
1195                 self.scale = Scaler(self, self.display)
1196                 status = self.status = Status(self)
1197                 self.scrollY = Scrollbar(self, orient="vertical",
1198                     command=self.display_yview)
1199                 self.display.scrollX = Scrollbar(self, orient="horizontal",
1200                     command=self.display.xview)
1201                 self.display["xscrollcommand"] = self.display.scrollX.set
1202                 self.display["yscrollcommand"] = self.scrollY.set
1203                 self.names["yscrollcommand"] = self.scrollY.set
1204
1205         def layout(self):
1206                 self.columnconfigure(1, weight=1)
1207                 self.rowconfigure(1, weight=1)
1208                 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1209                 self.names.grid(row=1, column=0, sticky=N+S)
1210                 self.display.grid(row=1, column=1, sticky=W+E+N+S)
1211                 self.scrollY.grid(row=1, column=2, sticky=N+S)
1212                 self.display.scrollX.grid(row=2, column=0, columnspan=2,
1213                     sticky=E+W)
1214                 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1215                 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1216
1217         def draw(self, file):
1218                 self.master.update()
1219                 ktrfile = KTRFile(file)
1220                 self.display.setfile(ktrfile)
1221                 self.display.drawnames(self.names)
1222                 self.display.draw()
1223                 self.scale.set(250000)
1224                 self.display.xview_moveto(0)
1225
1226         def display_yview(self, *args):
1227                 self.names.yview(*args)
1228                 self.display.yview(*args)
1229
1230         def setcolor(self, tag, color):
1231                 self.display.setcolor(tag, color)
1232
1233         def hide(self, tag):
1234                 self.display.hide(tag)
1235
1236 if (len(sys.argv) != 2):
1237         print "usage:", sys.argv[0], "<ktr file>"
1238         sys.exit(1)
1239
1240 root = Tk()
1241 root.title("Scheduler Graph")
1242 graph = SchedGraph(root)
1243 root.mainloop()