summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorjeff <jeff@FreeBSD.org>2004-12-26 00:13:07 +0000
committerjeff <jeff@FreeBSD.org>2004-12-26 00:13:07 +0000
commit02bd7382ccdccac7ddffe100522ec94f2ca09f44 (patch)
tree6cfe9b7e3e9c6368162e91cab4696accd824729b /tools
parented28e446cc9b01e6010f84d3be5fc5fac17d7e4e (diff)
downloadFreeBSD-src-02bd7382ccdccac7ddffe100522ec94f2ca09f44.zip
FreeBSD-src-02bd7382ccdccac7ddffe100522ec94f2ca09f44.tar.gz
- Add 'schedgraph' a scheduler trace visualization tool written with
python and tkinter. Schedgraph takes input from files produces by ktrdump -ct when KTR_SCHED is compiled into the kernel. The output represents the states of each thread with colored line segments as well as colored points for non-state scheduler events. Each line segment and point is clickable to obtain extra detail.
Diffstat (limited to 'tools')
-rw-r--r--tools/sched/schedgraph.py1209
1 files changed, 1209 insertions, 0 deletions
diff --git a/tools/sched/schedgraph.py b/tools/sched/schedgraph.py
new file mode 100644
index 0000000..25493a2
--- /dev/null
+++ b/tools/sched/schedgraph.py
@@ -0,0 +1,1209 @@
+#!/usr/local/bin/python
+
+# Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice unmodified, this list of conditions, and the following
+# disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+
+import sys
+import re
+from Tkinter import *
+
+# 1) Add a per-thread summary display
+# 2) Add bounding box style zoom.
+# 3) Click to center.
+# 4) Implement some sorting mechanism.
+
+ticksps = None
+status = None
+configtypes = []
+
+def ticks2sec(ticks):
+ ns = ticksps / 1000000000
+ ticks /= ns
+ if (ticks < 1000):
+ return (str(ticks) + "ns")
+ ticks /= 1000
+ if (ticks < 1000):
+ return (str(ticks) + "us")
+ ticks /= 1000
+ if (ticks < 1000):
+ return (str(ticks) + "ms")
+ ticks /= 1000
+ return (str(ticks) + "s")
+
+class Scaler(Frame):
+ def __init__(self, master, target):
+ Frame.__init__(self, master)
+ self.scale = Scale(self, command=self.scaleset,
+ from_=1000, to_=1000000, orient=HORIZONTAL, resolution=1000)
+ self.label = Label(self, text="Ticks per pixel")
+ self.label.pack(side=LEFT)
+ self.scale.pack(fill="both", expand=1)
+ self.target = target
+ self.scale.set(target.scaleget())
+ self.initialized = 1
+
+ def scaleset(self, value):
+ self.target.scaleset(int(value))
+
+ def set(self, value):
+ self.scale.set(value)
+
+class Status(Frame):
+ def __init__(self, master):
+ Frame.__init__(self, master)
+ self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
+ self.label.pack(fill="both", expand=1)
+ self.clear()
+
+ def set(self, str):
+ self.label.config(text=str)
+
+ def clear(self):
+ self.label.config(text="")
+
+ def startup(self, str):
+ self.set(str)
+ root.update()
+
+class EventConf(Frame):
+ def __init__(self, master, name, color, enabled):
+ Frame.__init__(self, master)
+ self.name = name
+ self.color = StringVar()
+ self.color_default = color
+ self.color_current = color
+ self.color.set(color)
+ self.enabled = IntVar()
+ self.enabled_default = enabled
+ self.enabled_current = enabled
+ self.enabled.set(enabled)
+ self.draw()
+
+ def draw(self):
+ self.label = Label(self, text=self.name, anchor=W)
+ self.sample = Canvas(self, width=24, height=24,
+ bg='grey')
+ self.rect = self.sample.create_rectangle(0, 0, 24, 24,
+ fill=self.color.get())
+ self.list = OptionMenu(self, self.color,
+ "dark red", "red", "pink",
+ "dark orange", "orange",
+ "yellow", "light yellow",
+ "dark green", "green", "light green",
+ "dark blue", "blue", "light blue",
+ "dark violet", "violet", "purple",
+ "dark grey", "light grey",
+ "white", "black",
+ command=self.setcolor)
+ self.checkbox = Checkbutton(self, text="enabled",
+ variable=self.enabled)
+ self.label.grid(row=0, column=0, sticky=E+W)
+ self.sample.grid(row=0, column=1)
+ self.list.grid(row=0, column=2, sticky=E+W)
+ self.checkbox.grid(row=0, column=3)
+ self.columnconfigure(0, weight=1)
+ self.columnconfigure(2, minsize=110)
+
+ def setcolor(self, color):
+ self.color.set(color)
+ self.sample.itemconfigure(self.rect, fill=color)
+
+ def apply(self):
+ cchange = 0
+ echange = 0
+ if (self.color_current != self.color.get()):
+ cchange = 1
+ if (self.enabled_current != self.enabled.get()):
+ echange = 1
+ self.color_current = self.color.get()
+ self.enabled_current = self.enabled.get()
+ if (echange != 0):
+ if (self.enabled_current):
+ graph.setcolor(self.name, self.color_current)
+ else:
+ graph.hide(self.name)
+ return
+ if (cchange != 0):
+ graph.setcolor(self.name, self.color_current)
+
+ def revert(self):
+ self.setcolor(self.color_current)
+ self.enabled.set(self.enabled_current)
+
+ def default(self):
+ self.setcolor(self.color_default)
+ self.enabled.set(self.enabled_default)
+
+class EventConfigure(Toplevel):
+ def __init__(self):
+ Toplevel.__init__(self)
+ self.resizable(0, 0)
+ self.title("Event Configuration")
+ self.items = LabelFrame(self, text="Event Type")
+ self.buttons = Frame(self)
+ self.drawbuttons()
+ self.items.grid(row=0, column=0, sticky=E+W)
+ self.columnconfigure(0, weight=1)
+ self.buttons.grid(row=1, column=0, sticky=E+W)
+ self.types = []
+ self.irow = 0
+ for type in configtypes:
+ self.additem(type.name, type.color, type.enabled)
+
+ def additem(self, name, color, enabled=1):
+ item = EventConf(self.items, name, color, enabled)
+ self.types.append(item)
+ item.grid(row=self.irow, column=0, sticky=E+W)
+ self.irow += 1
+
+ def drawbuttons(self):
+ self.apply = Button(self.buttons, text="Apply",
+ command=self.apress)
+ self.revert = Button(self.buttons, text="Revert",
+ command=self.rpress)
+ self.default = Button(self.buttons, text="Default",
+ command=self.dpress)
+ self.apply.grid(row=0, column=0, sticky=E+W)
+ self.revert.grid(row=0, column=1, sticky=E+W)
+ self.default.grid(row=0, column=2, sticky=E+W)
+ self.buttons.columnconfigure(0, weight=1)
+ self.buttons.columnconfigure(1, weight=1)
+ self.buttons.columnconfigure(2, weight=1)
+
+ def apress(self):
+ for item in self.types:
+ item.apply()
+
+ def rpress(self):
+ for item in self.types:
+ item.revert()
+
+ def dpress(self):
+ for item in self.types:
+ item.default()
+
+class EventView(Toplevel):
+ def __init__(self, event, canvas):
+ Toplevel.__init__(self)
+ self.resizable(0, 0)
+ self.title("Event")
+ self.event = event
+ self.frame = Frame(self)
+ self.frame.grid(row=0, column=0, sticky=N+S+E+W)
+ self.buttons = Frame(self)
+ self.buttons.grid(row=1, column=0, sticky=E+W)
+ self.canvas = canvas
+ self.drawlabels()
+ self.drawbuttons()
+ event.displayref(canvas)
+ self.bind("<Destroy>", self.destroycb)
+
+ def destroycb(self, event):
+ self.unbind("<Destroy>")
+ if (self.event != None):
+ self.event.displayunref(self.canvas)
+ self.event = None
+ self.destroy()
+
+ def clearlabels(self):
+ for label in self.frame.grid_slaves():
+ label.grid_remove()
+
+ def drawlabels(self):
+ ypos = 0
+ labels = self.event.labels()
+ while (len(labels) < 7):
+ labels.append(("", "", 0))
+ for label in labels:
+ name, value, linked = label
+ l = Label(self.frame, text=name, bd=1, width=15,
+ relief=SUNKEN, anchor=W)
+ if (linked):
+ fgcolor = "blue"
+ else:
+ fgcolor = "black"
+ r = Label(self.frame, text=value, bd=1,
+ relief=SUNKEN, anchor=W, fg=fgcolor)
+ l.grid(row=ypos, column=0, sticky=E+W)
+ r.grid(row=ypos, column=1, sticky=E+W)
+ if (linked):
+ r.bind("<Button-1>", self.linkpress)
+ ypos += 1
+ self.frame.columnconfigure(1, minsize=80)
+
+ def drawbuttons(self):
+ self.back = Button(self.buttons, text="<", command=self.bpress)
+ self.forw = Button(self.buttons, text=">", command=self.fpress)
+ self.new = Button(self.buttons, text="new", command=self.npress)
+ self.back.grid(row=0, column=0, sticky=E+W)
+ self.forw.grid(row=0, column=1, sticky=E+W)
+ self.new.grid(row=0, column=2, sticky=E+W)
+ self.buttons.columnconfigure(2, weight=1)
+
+ def newevent(self, event):
+ self.event.displayunref(self.canvas)
+ self.clearlabels()
+ self.event = event
+ self.event.displayref(self.canvas)
+ self.drawlabels()
+
+ def npress(self):
+ EventView(self.event, self.canvas)
+
+ def bpress(self):
+ prev = self.event.prev()
+ if (prev == None):
+ return
+ while (prev.real == 0):
+ prev = prev.prev()
+ if (prev == None):
+ return
+ self.newevent(prev)
+
+ def fpress(self):
+ next = self.event.next()
+ if (next == None):
+ return
+ while (next.real == 0):
+ next = next.next()
+ if (next == None):
+ return
+ self.newevent(next)
+
+ def linkpress(self, wevent):
+ event = self.event.getlinked()
+ if (event != None):
+ self.newevent(event)
+
+class Event:
+ name = "none"
+ color = "grey"
+ def __init__(self, source, cpu, timestamp, last=0):
+ self.source = source
+ self.cpu = cpu
+ self.timestamp = int(timestamp)
+ self.entries = []
+ self.real = 1
+ self.idx = None
+ self.state = 0
+ self.item = None
+ self.dispcnt = 0
+ self.linked = None
+ if (last):
+ source.lastevent(self)
+ else:
+ source.event(self)
+
+ def status(self):
+ statstr = self.name + " " + self.source.name
+ statstr += " on: cpu" + str(self.cpu)
+ statstr += " at: " + str(self.timestamp)
+ statstr += self.stattxt()
+ status.set(statstr)
+
+ def stattxt(self):
+ return ""
+
+ def textadd(self, tuple):
+ pass
+ self.entries.append(tuple)
+
+ def labels(self):
+ return [("Source:", self.source.name, 0),
+ ("Event:", self.name, 0),
+ ("CPU:", self.cpu, 0),
+ ("Timestamp:", self.timestamp, 0)] + self.entries
+ def mouseenter(self, canvas, item):
+ self.displayref(canvas)
+ self.status()
+
+ def mouseexit(self, canvas, item):
+ self.displayunref(canvas)
+ status.clear()
+
+ def mousepress(self, canvas, item):
+ EventView(self, canvas)
+
+ def next(self):
+ return self.source.eventat(self.idx + 1)
+
+ def prev(self):
+ return self.source.eventat(self.idx - 1)
+
+ def displayref(self, canvas):
+ if (self.dispcnt == 0):
+ canvas.itemconfigure(self.item, width=2)
+ self.dispcnt += 1
+
+ def displayunref(self, canvas):
+ self.dispcnt -= 1
+ if (self.dispcnt == 0):
+ canvas.itemconfigure(self.item, width=0)
+ canvas.tag_raise("point", "state")
+
+ def getlinked(self):
+ return self.linked.findevent(self.timestamp)
+
+class PointEvent(Event):
+ def __init__(self, thread, cpu, timestamp, last=0):
+ Event.__init__(self, thread, cpu, timestamp, last)
+
+ def draw(self, canvas, xpos, ypos):
+ l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11,
+ fill=self.color, tags=("all", "point", "event")
+ + (self.name,), width=0)
+ canvas.events[l] = self
+ self.item = l
+ if (self.enabled == 0):
+ canvas.itemconfigure(l, state="hidden")
+
+ return (xpos)
+
+class StateEvent(Event):
+ def __init__(self, thread, cpu, timestamp, last=0):
+ Event.__init__(self, thread, cpu, timestamp, last)
+ self.duration = 0
+ self.skipnext = 0
+ self.skipself = 0
+ self.state = 1
+
+ def draw(self, canvas, xpos, ypos):
+ next = self.nextstate()
+ if (self.skipself == 1 or next == None):
+ return (xpos)
+ if (self.skipnext):
+ skipped = next
+ next.skipself = 1
+ next.real = 0
+ next = next.nextstate()
+ if (next == None):
+ next = skipped
+ self.duration = next.timestamp - self.timestamp
+ delta = self.duration / canvas.ratio
+ l = canvas.create_rectangle(xpos, ypos,
+ xpos + delta, ypos - 10, fill=self.color, width=0,
+ tags=("all", "state", "event") + (self.name,))
+ canvas.events[l] = self
+ self.item = l
+ if (self.enabled == 0):
+ canvas.itemconfigure(l, state="hidden")
+
+ return (xpos + delta)
+
+ def stattxt(self):
+ return " duration: " + ticks2sec(self.duration)
+
+ def nextstate(self):
+ next = self.next()
+ while (next != None and next.state == 0):
+ next = next.next()
+ return (next)
+
+ def labels(self):
+ return [("Source:", self.source.name, 0),
+ ("Event:", self.name, 0),
+ ("Timestamp:", self.timestamp, 0),
+ ("CPU:", self.cpu, 0),
+ ("Duration:", ticks2sec(self.duration), 0)] \
+ + self.entries
+
+class Count(Event):
+ name = "Count"
+ color = "red"
+ enabled = 1
+ def __init__(self, source, cpu, timestamp, count):
+ self.count = int(count)
+ Event.__init__(self, source, cpu, timestamp)
+ self.duration = 0
+ self.textadd(("count:", self.count, 0))
+
+ def draw(self, canvas, xpos, ypos):
+ next = self.next()
+ self.duration = next.timestamp - self.timestamp
+ delta = self.duration / canvas.ratio
+ yhight = self.source.yscale() * self.count
+ l = canvas.create_rectangle(xpos, ypos - yhight,
+ xpos + delta, ypos, fill=self.color, width=0,
+ tags=("all", "count", "event") + (self.name,))
+ canvas.events[l] = self
+ self.item = l
+ if (self.enabled == 0):
+ canvas.itemconfigure(l, state="hidden")
+ return (xpos + delta)
+
+ def stattxt(self):
+ return " count: " + str(self.count)
+
+configtypes.append(Count)
+
+class Running(StateEvent):
+ name = "running"
+ color = "green"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Running)
+
+class Idle(StateEvent):
+ name = "idle"
+ color = "grey"
+ enabled = 0
+ def __init__(self, thread, cpu, timestamp, prio):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Idle)
+
+class Yielding(StateEvent):
+ name = "yielding"
+ color = "yellow"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Yielding)
+
+class Swapped(StateEvent):
+ name = "swapped"
+ color = "violet"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Swapped)
+
+class Suspended(StateEvent):
+ name = "suspended"
+ color = "purple"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Suspended)
+
+class Iwait(StateEvent):
+ name = "iwait"
+ color = "grey"
+ enabled = 0
+ def __init__(self, thread, cpu, timestamp, prio):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Iwait)
+
+class Preempted(StateEvent):
+ name = "preempted"
+ color = "red"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio, bythread):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.skipnext = 1
+ self.prio = prio
+ self.linked = bythread
+ self.textadd(("prio:", self.prio, 0))
+ self.textadd(("by thread:", self.linked.name, 1))
+
+configtypes.append(Preempted)
+
+class Sleep(StateEvent):
+ name = "sleep"
+ color = "blue"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio, wmesg):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.wmesg = wmesg
+ self.textadd(("prio:", self.prio, 0))
+ self.textadd(("wmesg:", self.wmesg, 0))
+
+ def stattxt(self):
+ statstr = StateEvent.stattxt(self)
+ statstr += " sleeping on: " + self.wmesg
+ return (statstr)
+
+configtypes.append(Sleep)
+
+class Blocked(StateEvent):
+ name = "blocked"
+ color = "dark red"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio, lock):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.lock = lock
+ self.textadd(("prio:", self.prio, 0))
+ self.textadd(("lock:", self.lock, 0))
+
+ def stattxt(self):
+ statstr = StateEvent.stattxt(self)
+ statstr += " blocked on: " + self.lock
+ return (statstr)
+
+configtypes.append(Blocked)
+
+class KsegrpRunq(StateEvent):
+ name = "KsegrpRunq"
+ color = "orange"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio, bythread):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.linked = bythread
+ self.textadd(("prio:", self.prio, 0))
+ self.textadd(("by thread:", self.linked.name, 1))
+
+configtypes.append(KsegrpRunq)
+
+class Runq(StateEvent):
+ name = "Runq"
+ color = "yellow"
+ enabled = 1
+ def __init__(self, thread, cpu, timestamp, prio, bythread):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.linked = bythread
+ self.textadd(("prio:", self.prio, 0))
+ self.textadd(("by thread:", self.linked.name, 1))
+
+configtypes.append(Runq)
+
+class Sched_exit(StateEvent):
+ name = "exit"
+ color = "grey"
+ enabled = 0
+ def __init__(self, thread, cpu, timestamp, prio):
+ StateEvent.__init__(self, thread, cpu, timestamp)
+ self.name = "sched_exit"
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Sched_exit)
+
+class Padevent(StateEvent):
+ def __init__(self, thread, cpu, timestamp, last=0):
+ StateEvent.__init__(self, thread, cpu, timestamp, last)
+ self.name = "pad"
+ self.real = 0
+
+ def draw(self, canvas, xpos, ypos):
+ next = self.next()
+ if (next == None):
+ return (xpos)
+ self.duration = next.timestamp - self.timestamp
+ delta = self.duration / canvas.ratio
+ return (xpos + delta)
+
+class Tick(PointEvent):
+ name = "tick"
+ color = "black"
+ enabled = 0
+ def __init__(self, thread, cpu, timestamp, prio, stathz):
+ PointEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.textadd(("prio:", self.prio, 0))
+
+configtypes.append(Tick)
+
+class Prio(PointEvent):
+ name = "prio"
+ color = "black"
+ enabled = 0
+ def __init__(self, thread, cpu, timestamp, prio, newprio, bythread):
+ PointEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.newprio = newprio
+ self.linked = bythread
+ self.textadd(("new prio:", self.newprio, 0))
+ self.textadd(("prio:", self.prio, 0))
+ if (self.linked != self.source):
+ self.textadd(("by thread:", self.linked.name, 1))
+ else:
+ self.textadd(("by thread:", self.linked.name, 0))
+
+configtypes.append(Prio)
+
+class Lend(PointEvent):
+ name = "lend"
+ color = "black"
+ enabled = 0
+ def __init__(self, thread, cpu, timestamp, prio, tothread):
+ PointEvent.__init__(self, thread, cpu, timestamp)
+ self.prio = prio
+ self.linked = tothread
+ self.textadd(("prio:", self.prio, 0))
+ self.textadd(("to thread:", self.linked.name, 1))
+
+configtypes.append(Lend)
+
+class Wokeup(PointEvent):
+ name = "wokeup"
+ color = "black"
+ enabled = 0
+ def __init__(self, thread, cpu, timestamp, ranthread):
+ PointEvent.__init__(self, thread, cpu, timestamp)
+ self.linked = ranthread
+ self.textadd(("ran thread:", self.linked.name, 1))
+
+configtypes.append(Wokeup)
+
+class EventSource:
+ def __init__(self, name):
+ self.name = name
+ self.events = []
+ self.cpu = 0
+ self.cpux = 0
+
+ def fixup(self):
+ pass
+
+ def event(self, event):
+ self.events.insert(0, event)
+
+ def remove(self, event):
+ self.events.remove(event)
+
+ def lastevent(self, event):
+ self.events.append(event)
+
+ def draw(self, canvas, ypos):
+ xpos = 10
+ self.cpux = 10
+ self.cpu = self.events[1].cpu
+ for i in range(0, len(self.events)):
+ self.events[i].idx = i
+ for event in self.events:
+ if (event.cpu != self.cpu and event.cpu != -1):
+ self.drawcpu(canvas, xpos, ypos)
+ self.cpux = xpos
+ self.cpu = event.cpu
+ xpos = event.draw(canvas, xpos, ypos)
+ self.drawcpu(canvas, xpos, ypos)
+
+ def drawname(self, canvas, ypos):
+ ypos = ypos - (self.ysize() / 2)
+ canvas.create_text(10, ypos, anchor="w", text=self.name)
+
+ def drawcpu(self, canvas, xpos, ypos):
+ cpu = int(self.cpu)
+ if (cpu == 0):
+ color = 'light grey'
+ elif (cpu == 1):
+ color = 'dark grey'
+ elif (cpu == 2):
+ color = 'light blue'
+ elif (cpu == 3):
+ color == 'light green'
+ else:
+ color == "white"
+ l = canvas.create_rectangle(self.cpux,
+ ypos - self.ysize() - canvas.bdheight,
+ xpos, ypos + canvas.bdheight, fill=color, width=0,
+ tags=("all", "cpuinfo"))
+
+ def ysize(self):
+ return (None)
+
+ def eventat(self, i):
+ if (i >= len(self.events)):
+ return (None)
+ event = self.events[i]
+ return (event)
+
+ def findevent(self, timestamp):
+ for event in self.events:
+ if (event.timestamp >= timestamp and event.real):
+ return (event)
+ return (None)
+
+class Thread(EventSource):
+ names = {}
+ def __init__(self, td, pcomm):
+ EventSource.__init__(self, pcomm)
+ self.str = td
+ try:
+ cnt = Thread.names[pcomm]
+ except:
+ Thread.names[pcomm] = 0
+ return
+ Thread.names[pcomm] = cnt + 1
+
+ def fixup(self):
+ cnt = Thread.names[self.name]
+ if (cnt == 0):
+ return
+ cnt -= 1
+ Thread.names[self.name] = cnt
+ self.name += " td" + str(cnt)
+
+ def ysize(self):
+ return (10)
+
+class Counter(EventSource):
+ max = 0
+ def __init__(self, name):
+ EventSource.__init__(self, name)
+
+ def event(self, event):
+ EventSource.event(self, event)
+ try:
+ count = event.count
+ except:
+ return
+ count = int(count)
+ if (count > Counter.max):
+ Counter.max = count
+
+ def ysize(self):
+ return (80)
+
+ def yscale(self):
+ return (self.ysize() / Counter.max)
+
+
+class KTRFile:
+ def __init__(self, file):
+ self.timestamp_first = None
+ self.timestamp_last = None
+ self.lineno = -1
+ self.threads = []
+ self.sources = []
+ self.ticks = {}
+ self.load = {}
+
+ self.parse(file)
+ self.fixup()
+ global ticksps
+ ticksps = self.ticksps()
+
+ def parse(self, file):
+ try:
+ ifp = open(file)
+ except:
+ print "Can't open", file
+ sys.exit(1)
+
+ ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+"
+ tdname = "(\S+)\(([^)]*)\)"
+
+ ktrstr = "mi_switch: " + tdname
+ ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)"
+ switchout_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "mi_switch: " + tdname + " prio (\d+) idle"
+ idled_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by "
+ ktrstr += tdname
+ preempted_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "mi_switch: running " + tdname + " prio (\d+)"
+ switchin_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname
+ sched_add_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname
+ setrunqueue_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname
+ sched_rem_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)"
+ sched_exit_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "statclock: " + tdname + " prio (\d+)"
+ ktrstr += " stathz (\d+)"
+ sched_clock_re = re.compile(ktrhdr + ktrstr)
+
+ ktrstr = "sched_prio: " + tdname + " prio (\d+)"
+ ktrstr += " newprio (\d+) by " + tdname
+ sched_prio_re = re.compile(ktrhdr + ktrstr)
+
+ cpuload_re = re.compile(ktrhdr + "load: (\d)")
+ loadglobal_re = re.compile(ktrhdr + "global load: (\d)")
+
+ parsers = [[cpuload_re, self.cpuload],
+ [loadglobal_re, self.loadglobal],
+ [switchin_re, self.switchin],
+ [switchout_re, self.switchout],
+ [sched_add_re, self.sched_add],
+ [setrunqueue_re, self.sched_rem],
+ [sched_prio_re, self.sched_prio],
+ [preempted_re, self.preempted],
+ [sched_rem_re, self.sched_rem],
+ [sched_exit_re, self.sched_exit],
+ [sched_clock_re, self.sched_clock],
+ [idled_re, self.idled]]
+
+ for line in ifp.readlines():
+ self.lineno += 1
+ if ((self.lineno % 1024) == 0):
+ status.startup("Parsing line " +
+ str(self.lineno))
+ for p in parsers:
+ m = p[0].match(line)
+ if (m != None):
+ p[1](*m.groups())
+ break
+ # if (m == None):
+ # print line,
+
+ def checkstamp(self, timestamp):
+ timestamp = int(timestamp)
+ if (self.timestamp_first == None):
+ self.timestamp_first = timestamp
+ if (timestamp > self.timestamp_first):
+ print "Bad timestamp on line ", self.lineno
+ return (0)
+ self.timestamp_last = timestamp
+ return (1)
+
+ def timespan(self):
+ return (self.timestamp_first - self.timestamp_last);
+
+ def ticksps(self):
+ return (self.timespan() / self.ticks[0]) * int(self.stathz)
+
+ def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock):
+ TDI_SUSPENDED = 0x0001
+ TDI_SLEEPING = 0x0002
+ TDI_SWAPPED = 0x0004
+ TDI_LOCK = 0x0008
+ TDI_IWAIT = 0x0010
+
+ if (self.checkstamp(timestamp) == 0):
+ return
+ inhibit = int(inhibit)
+ thread = self.findtd(td, pcomm)
+ if (inhibit & TDI_SWAPPED):
+ Swapped(thread, cpu, timestamp, prio)
+ elif (inhibit & TDI_SLEEPING):
+ Sleep(thread, cpu, timestamp, prio, wmesg)
+ elif (inhibit & TDI_LOCK):
+ Blocked(thread, cpu, timestamp, prio, lock)
+ elif (inhibit & TDI_IWAIT):
+ Iwait(thread, cpu, timestamp, prio)
+ elif (inhibit & TDI_SUSPENDED):
+ Suspended(thread, cpu, timestamp, prio)
+ elif (inhibit == 0):
+ Yielding(thread, cpu, timestamp, prio)
+ else:
+ print "Unknown event", inhibit
+ sys.exit(1)
+
+ def idled(self, cpu, timestamp, td, pcomm, prio):
+ if (self.checkstamp(timestamp) == 0):
+ return
+ thread = self.findtd(td, pcomm)
+ Idle(thread, cpu, timestamp, prio)
+
+ def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
+ if (self.checkstamp(timestamp) == 0):
+ return
+ thread = self.findtd(td, pcomm)
+ Preempted(thread, cpu, timestamp, prio,
+ self.findtd(bytd, bypcomm))
+
+ def switchin(self, cpu, timestamp, td, pcomm, prio):
+ if (self.checkstamp(timestamp) == 0):
+ return
+ thread = self.findtd(td, pcomm)
+ Running(thread, cpu, timestamp, prio)
+
+ def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
+ if (self.checkstamp(timestamp) == 0):
+ return
+ thread = self.findtd(td, pcomm)
+ bythread = self.findtd(bytd, bypcomm)
+ Runq(thread, cpu, timestamp, prio, bythread)
+ Wokeup(bythread, cpu, timestamp, thread)
+
+ def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm):
+ if (self.checkstamp(timestamp) == 0):
+ return
+ thread = self.findtd(td, pcomm)
+ KsegrpRunq(thread, cpu, timestamp, prio,
+ self.findtd(bytd, bypcomm))
+
+ def sched_exit(self, cpu, timestamp, td, pcomm, prio):
+ if (self.checkstamp(timestamp) == 0):
+ return
+ thread = self.findtd(td, pcomm)
+ Sched_exit(thread, cpu, timestamp, prio)
+
+ def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz):
+ if (self.checkstamp(timestamp) == 0):
+ return
+ self.stathz = stathz
+ cpu = int(cpu)
+ try:
+ ticks = self.ticks[cpu]
+ except:
+ self.ticks[cpu] = 0
+ self.ticks[cpu] += 1
+ thread = self.findtd(td, pcomm)
+ Tick(thread, cpu, timestamp, prio, stathz)
+
+ def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm):
+ if (prio == newprio):
+ return
+ if (self.checkstamp(timestamp) == 0):
+ return
+ thread = self.findtd(td, pcomm)
+ bythread = self.findtd(bytd, bypcomm)
+ Prio(thread, cpu, timestamp, prio, newprio, bythread)
+ Lend(bythread, cpu, timestamp, newprio, thread)
+
+ def cpuload(self, cpu, timestamp, count):
+ cpu = int(cpu)
+ try:
+ load = self.load[cpu]
+ except:
+ load = Counter("cpu" + str(cpu) + " load")
+ self.load[cpu] = load
+ self.sources.insert(0, load)
+ Count(load, cpu, timestamp, count)
+
+ def loadglobal(self, cpu, timestamp, count):
+ cpu = 0
+ try:
+ load = self.load[cpu]
+ except:
+ load = Counter("CPU load")
+ self.load[cpu] = load
+ self.sources.insert(0, load)
+ Count(load, cpu, timestamp, count)
+
+ def findtd(self, td, pcomm):
+ for thread in self.threads:
+ if (thread.str == td and thread.name == pcomm):
+ return thread
+ thread = Thread(td, pcomm)
+ self.threads.append(thread)
+ self.sources.append(thread)
+ return (thread)
+
+ def fixup(self):
+ for source in self.sources:
+ Padevent(source, -1, self.timestamp_last)
+ Padevent(source, -1, self.timestamp_first, last=1)
+ source.fixup()
+
+class SchedDisplay(Canvas):
+ def __init__(self, master):
+ self.ratio = 1
+ self.ktrfile = None
+ self.sources = None
+ self.bdheight = 10
+ self.events = {}
+
+ Canvas.__init__(self, master, width=800, height=500, bg='grey',
+ scrollregion=(0, 0, 800, 500))
+
+ def setfile(self, ktrfile):
+ self.ktrfile = ktrfile
+ self.sources = ktrfile.sources
+
+ def draw(self):
+ ypos = 0
+ xsize = self.xsize()
+ for source in self.sources:
+ status.startup("Drawing " + source.name)
+ self.create_line(0, ypos, xsize, ypos,
+ width=1, fill="black", tags=("all",))
+ ypos += self.bdheight
+ ypos += source.ysize()
+ source.draw(self, ypos)
+ ypos += self.bdheight
+ try:
+ self.tag_raise("point", "state")
+ self.tag_lower("cpuinfo", "all")
+ except:
+ pass
+ self.create_line(0, ypos, xsize, ypos,
+ width=1, fill="black", tags=("all",))
+ self.tag_bind("event", "<Enter>", self.mouseenter)
+ self.tag_bind("event", "<Leave>", self.mouseexit)
+ self.tag_bind("event", "<Button-1>", self.mousepress)
+
+ def mouseenter(self, event):
+ item, = self.find_withtag(CURRENT)
+ event = self.events[item]
+ event.mouseenter(self, item)
+
+ def mouseexit(self, event):
+ item, = self.find_withtag(CURRENT)
+ event = self.events[item]
+ event.mouseexit(self, item)
+
+ def mousepress(self, event):
+ item, = self.find_withtag(CURRENT)
+ event = self.events[item]
+ event.mousepress(self, item)
+
+ def drawnames(self, canvas):
+ status.startup("Drawing names")
+ ypos = 0
+ canvas.configure(scrollregion=(0, 0,
+ canvas["width"], self.ysize()))
+ for source in self.sources:
+ canvas.create_line(0, ypos, canvas["width"], ypos,
+ width=1, fill="black", tags=("all",))
+ ypos += self.bdheight
+ ypos += source.ysize()
+ source.drawname(canvas, ypos)
+ ypos += self.bdheight
+ canvas.create_line(0, ypos, canvas["width"], ypos,
+ width=1, fill="black", tags=("all",))
+
+ def xsize(self):
+ return ((self.ktrfile.timespan() / self.ratio) + 20)
+
+ def ysize(self):
+ ysize = 0
+ for source in self.sources:
+ ysize += source.ysize() + (self.bdheight * 2)
+ return (ysize)
+
+ def scaleset(self, ratio):
+ if (self.ktrfile == None):
+ return
+ oldratio = self.ratio
+ xstart, ystart = self.xview()
+ length = (float(self["width"]) / self.xsize())
+ middle = xstart + (length / 2)
+
+ self.ratio = ratio
+ self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
+ self.scale("all", 0, 0, float(oldratio) / ratio, 1)
+
+ length = (float(self["width"]) / self.xsize())
+ xstart = middle - (length / 2)
+ self.xview_moveto(xstart)
+
+ def scaleget(self):
+ return self.ratio
+
+ def setcolor(self, tag, color):
+ self.itemconfigure(tag, state="normal", fill=color)
+
+ def hide(self, tag):
+ self.itemconfigure(tag, state="hidden")
+
+class GraphMenu(Frame):
+ def __init__(self, master):
+ Frame.__init__(self, master, bd=2, relief=RAISED)
+ self.view = Menubutton(self, text="Configure")
+ self.viewmenu = Menu(self.view, tearoff=0)
+ self.viewmenu.add_command(label="Events",
+ command=self.econf)
+ self.view["menu"] = self.viewmenu
+ self.view.pack(side=LEFT)
+
+ def econf(self):
+ EventConfigure()
+
+
+class SchedGraph(Frame):
+ def __init__(self, master):
+ Frame.__init__(self, master)
+ self.menu = None
+ self.names = None
+ self.display = None
+ self.scale = None
+ self.status = None
+ self.pack(expand=1, fill="both")
+ self.buildwidgets()
+ self.layout()
+ self.draw(sys.argv[1])
+
+ def buildwidgets(self):
+ global status
+ self.menu = GraphMenu(self)
+ self.display = SchedDisplay(self)
+ self.names = Canvas(self,
+ width=100, height=self.display["height"],
+ bg='grey', scrollregion=(0, 0, 50, 100))
+ self.scale = Scaler(self, self.display)
+ status = self.status = Status(self)
+ self.scrollY = Scrollbar(self, orient="vertical",
+ command=self.display_yview)
+ self.display.scrollX = Scrollbar(self, orient="horizontal",
+ command=self.display.xview)
+ self.display["xscrollcommand"] = self.display.scrollX.set
+ self.display["yscrollcommand"] = self.scrollY.set
+ self.names["yscrollcommand"] = self.scrollY.set
+
+ def layout(self):
+ self.columnconfigure(1, weight=1)
+ self.rowconfigure(1, weight=1)
+ self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
+ self.names.grid(row=1, column=0, sticky=N+S)
+ self.display.grid(row=1, column=1, sticky=W+E+N+S)
+ self.scrollY.grid(row=1, column=2, sticky=N+S)
+ self.display.scrollX.grid(row=2, column=0, columnspan=2,
+ sticky=E+W)
+ self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
+ self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
+
+ def draw(self, file):
+ self.master.update()
+ ktrfile = KTRFile(file)
+ self.display.setfile(ktrfile)
+ self.display.drawnames(self.names)
+ self.display.draw()
+ self.scale.set(250000)
+ self.display.xview_moveto(0)
+
+ def display_yview(self, *args):
+ self.names.yview(*args)
+ self.display.yview(*args)
+
+ def setcolor(self, tag, color):
+ self.display.setcolor(tag, color)
+
+ def hide(self, tag):
+ self.display.hide(tag)
+
+if (len(sys.argv) != 2):
+ print "usage:", sys.argv[0], "<ktr file>"
+ sys.exit(1)
+
+root = Tk()
+root.title("Scheduler Graph")
+graph = SchedGraph(root)
+root.mainloop()
OpenPOWER on IntegriCloud