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