diff options
author | jeff <jeff@FreeBSD.org> | 2004-12-26 00:13:07 +0000 |
---|---|---|
committer | jeff <jeff@FreeBSD.org> | 2004-12-26 00:13:07 +0000 |
commit | 02bd7382ccdccac7ddffe100522ec94f2ca09f44 (patch) | |
tree | 6cfe9b7e3e9c6368162e91cab4696accd824729b /tools/sched/schedgraph.py | |
parent | ed28e446cc9b01e6010f84d3be5fc5fac17d7e4e (diff) | |
download | FreeBSD-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/sched/schedgraph.py')
-rw-r--r-- | tools/sched/schedgraph.py | 1209 |
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() |