1139313Sjeff#!/usr/local/bin/python 2139313Sjeff 3187580Sjeff# Copyright (c) 2002-2003, 2009, Jeffrey Roberson <jeff@freebsd.org> 4139313Sjeff# All rights reserved. 5139313Sjeff# 6139313Sjeff# Redistribution and use in source and binary forms, with or without 7139313Sjeff# modification, are permitted provided that the following conditions 8139313Sjeff# are met: 9139313Sjeff# 1. Redistributions of source code must retain the above copyright 10139313Sjeff# notice unmodified, this list of conditions, and the following 11139313Sjeff# disclaimer. 12139313Sjeff# 2. Redistributions in binary form must reproduce the above copyright 13139313Sjeff# notice, this list of conditions and the following disclaimer in the 14139313Sjeff# documentation and/or other materials provided with the distribution. 15139313Sjeff# 16139313Sjeff# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17139313Sjeff# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18139313Sjeff# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19139313Sjeff# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20139313Sjeff# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21139313Sjeff# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22139313Sjeff# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23139313Sjeff# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24139313Sjeff# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25139313Sjeff# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26139313Sjeff# 27139313Sjeff# $FreeBSD$ 28139313Sjeff 29139313Sjeffimport sys 30139313Sjeffimport re 31187358Sjeffimport random 32139313Sjefffrom Tkinter import * 33139313Sjeff 34139366Sarr# To use: 35173743Ssam# - Install the ports/x11-toolkits/py-tkinter package; e.g. 36173743Ssam# portinstall x11-toolkits/py-tkinter package 37173743Ssam# - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF; e.g. 38173743Ssam# options KTR 39173743Ssam# options KTR_ENTRIES=32768 40173743Ssam# options KTR_COMPILE=(KTR_SCHED) 41173743Ssam# options KTR_MASK=(KTR_SCHED) 42173743Ssam# - It is encouraged to increase KTR_ENTRIES size to gather enough 43173743Ssam# information for analysis; e.g. 44173743Ssam# options KTR_ENTRIES=262144 45175306Skris# as 32768 entries may only correspond to a second or two of profiling 46175306Skris# data depending on your workload. 47168940Skris# - Rebuild kernel with proper changes to KERNCONF and boot new kernel. 48168940Skris# - Run your workload to be profiled. 49168940Skris# - While the workload is continuing (i.e. before it finishes), disable 50168940Skris# KTR tracing by setting 'sysctl debug.ktr.mask=0'. This is necessary 51168940Skris# to avoid a race condition while running ktrdump, i.e. the KTR ring buffer 52168940Skris# will cycle a bit while ktrdump runs, and this confuses schedgraph because 53168940Skris# the timestamps appear to go backwards at some point. Stopping KTR logging 54168940Skris# while the workload is still running is to avoid wasting log entries on 55168940Skris# "idle" time at the end. 56139366Sarr# - Dump the trace to a file: 'ktrdump -ct > ktr.out' 57187358Sjeff# - Run the python script: 'python schedgraph.py ktr.out' optionally provide 58187358Sjeff# your cpu frequency in ghz: 'python schedgraph.py ktr.out 2.4' 59139366Sarr# 60139366Sarr# To do: 61187358Sjeff# Add a per-source summary display 62187358Sjeff# "Vertical rule" to help relate data in different rows 63187358Sjeff# Mouse-over popup of full thread/event/row label (currently truncated) 64187358Sjeff# More visible anchors for popup event windows 65168940Skris# 66168940Skris# BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of 67168940Skris# colours to represent them ;-) 68139313Sjeff 69187358Sjeffeventcolors = [ 70187358Sjeff ("count", "red"), 71187358Sjeff ("running", "green"), 72187358Sjeff ("idle", "grey"), 73278694Ssbruno ("spinning", "red"), 74187358Sjeff ("yielding", "yellow"), 75187358Sjeff ("swapped", "violet"), 76187358Sjeff ("suspended", "purple"), 77187358Sjeff ("iwait", "grey"), 78187358Sjeff ("sleep", "blue"), 79187358Sjeff ("blocked", "dark red"), 80187358Sjeff ("runq add", "yellow"), 81187358Sjeff ("runq rem", "yellow"), 82187358Sjeff ("thread exit", "grey"), 83187358Sjeff ("proc exit", "grey"), 84187358Sjeff ("lock acquire", "blue"), 85187358Sjeff ("lock contest", "purple"), 86187358Sjeff ("failed lock try", "red"), 87187358Sjeff ("lock release", "grey"), 88187379Sjeff ("statclock", "black"), 89187358Sjeff ("prio", "black"), 90187358Sjeff ("lend prio", "black"), 91187358Sjeff ("wokeup", "black") 92187358Sjeff] 93187358Sjeff 94187358Sjeffcpucolors = [ 95187358Sjeff ("CPU 0", "light grey"), 96187358Sjeff ("CPU 1", "dark grey"), 97187358Sjeff ("CPU 2", "light blue"), 98187358Sjeff ("CPU 3", "light pink"), 99187358Sjeff ("CPU 4", "blanched almond"), 100187358Sjeff ("CPU 5", "slate grey"), 101187358Sjeff ("CPU 6", "tan"), 102187358Sjeff ("CPU 7", "thistle"), 103187358Sjeff ("CPU 8", "white") 104187358Sjeff] 105187358Sjeff 106187358Sjeffcolors = [ 107187358Sjeff "white", "thistle", "blanched almond", "tan", "chartreuse", 108187358Sjeff "dark red", "red", "pale violet red", "pink", "light pink", 109187358Sjeff "dark orange", "orange", "coral", "light coral", 110187358Sjeff "goldenrod", "gold", "yellow", "light yellow", 111187358Sjeff "dark green", "green", "light green", "light sea green", 112187358Sjeff "dark blue", "blue", "light blue", "steel blue", "light slate blue", 113187358Sjeff "dark violet", "violet", "purple", "blue violet", 114187358Sjeff "dark grey", "slate grey", "light grey", 115187358Sjeff "black", 116187358Sjeff] 117187358Sjeffcolors.sort() 118187358Sjeff 119139313Sjeffticksps = None 120139313Sjeffstatus = None 121187358Sjeffcolormap = None 122187358Sjeffktrfile = None 123187358Sjeffclockfreq = None 124187358Sjeffsources = [] 125173743Ssamlineno = -1 126139313Sjeff 127187379SjeffY_BORDER = 10 128187379SjeffX_BORDER = 10 129187379SjeffY_COUNTER = 80 130187379SjeffY_EVENTSOURCE = 10 131187379SjeffXY_POINT = 4 132187379Sjeff 133187358Sjeffclass Colormap: 134187358Sjeff def __init__(self, table): 135187358Sjeff self.table = table 136187358Sjeff self.map = {} 137187358Sjeff for entry in table: 138187358Sjeff self.map[entry[0]] = entry[1] 139187358Sjeff 140187358Sjeff def lookup(self, name): 141187358Sjeff try: 142187358Sjeff color = self.map[name] 143187358Sjeff except: 144187358Sjeff color = colors[random.randrange(0, len(colors))] 145187358Sjeff print "Picking random color", color, "for", name 146187358Sjeff self.map[name] = color 147187358Sjeff self.table.append((name, color)) 148187358Sjeff return (color) 149187358Sjeff 150139313Sjeffdef ticks2sec(ticks): 151187580Sjeff ticks = float(ticks) 152187580Sjeff ns = float(ticksps) / 1000000000 153187580Sjeff ticks /= ns 154139313Sjeff if (ticks < 1000): 155187580Sjeff return ("%.2fns" % ticks) 156139313Sjeff ticks /= 1000 157139313Sjeff if (ticks < 1000): 158187580Sjeff return ("%.2fus" % ticks) 159139313Sjeff ticks /= 1000 160187580Sjeff if (ticks < 1000): 161187580Sjeff return ("%.2fms" % ticks) 162187580Sjeff ticks /= 1000 163187580Sjeff return ("%.2fs" % ticks) 164139313Sjeff 165139313Sjeffclass Scaler(Frame): 166139313Sjeff def __init__(self, master, target): 167139313Sjeff Frame.__init__(self, master) 168187471Sjeff self.scale = None 169187471Sjeff self.target = target 170139313Sjeff self.label = Label(self, text="Ticks per pixel") 171139313Sjeff self.label.pack(side=LEFT) 172187471Sjeff self.resolution = 100 173187471Sjeff self.setmax(10000) 174139313Sjeff 175139313Sjeff def scaleset(self, value): 176139313Sjeff self.target.scaleset(int(value)) 177139313Sjeff 178139313Sjeff def set(self, value): 179139313Sjeff self.scale.set(value) 180139313Sjeff 181187471Sjeff def setmax(self, value): 182187471Sjeff # 183187471Sjeff # We can't reconfigure the to_ value so we delete the old 184187471Sjeff # window and make a new one when we resize. 185187471Sjeff # 186187471Sjeff if (self.scale != None): 187187471Sjeff self.scale.pack_forget() 188187471Sjeff self.scale.destroy() 189187471Sjeff self.scale = Scale(self, command=self.scaleset, 190187471Sjeff from_=100, to_=value, orient=HORIZONTAL, 191187471Sjeff resolution=self.resolution) 192187471Sjeff self.scale.pack(fill="both", expand=1) 193187471Sjeff self.scale.set(self.target.scaleget()) 194187471Sjeff 195139313Sjeffclass Status(Frame): 196139313Sjeff def __init__(self, master): 197139313Sjeff Frame.__init__(self, master) 198139313Sjeff self.label = Label(self, bd=1, relief=SUNKEN, anchor=W) 199139313Sjeff self.label.pack(fill="both", expand=1) 200139313Sjeff self.clear() 201139313Sjeff 202139313Sjeff def set(self, str): 203139313Sjeff self.label.config(text=str) 204139313Sjeff 205139313Sjeff def clear(self): 206139313Sjeff self.label.config(text="") 207139313Sjeff 208139313Sjeff def startup(self, str): 209139313Sjeff self.set(str) 210139313Sjeff root.update() 211139313Sjeff 212187358Sjeffclass ColorConf(Frame): 213187358Sjeff def __init__(self, master, name, color): 214139313Sjeff Frame.__init__(self, master) 215187358Sjeff if (graph.getstate(name) == "hidden"): 216187358Sjeff enabled = 0 217187358Sjeff else: 218187358Sjeff enabled = 1 219139313Sjeff self.name = name 220139313Sjeff self.color = StringVar() 221139313Sjeff self.color_default = color 222139313Sjeff self.color_current = color 223139313Sjeff self.color.set(color) 224139313Sjeff self.enabled = IntVar() 225139313Sjeff self.enabled_default = enabled 226139313Sjeff self.enabled_current = enabled 227139313Sjeff self.enabled.set(enabled) 228139313Sjeff self.draw() 229139313Sjeff 230139313Sjeff def draw(self): 231139313Sjeff self.label = Label(self, text=self.name, anchor=W) 232139313Sjeff self.sample = Canvas(self, width=24, height=24, 233139313Sjeff bg='grey') 234139313Sjeff self.rect = self.sample.create_rectangle(0, 0, 24, 24, 235139313Sjeff fill=self.color.get()) 236187358Sjeff self.list = OptionMenu(self, self.color, command=self.setcolor, 237187358Sjeff *colors) 238139313Sjeff self.checkbox = Checkbutton(self, text="enabled", 239139313Sjeff variable=self.enabled) 240139313Sjeff self.label.grid(row=0, column=0, sticky=E+W) 241139313Sjeff self.sample.grid(row=0, column=1) 242139313Sjeff self.list.grid(row=0, column=2, sticky=E+W) 243139313Sjeff self.checkbox.grid(row=0, column=3) 244139313Sjeff self.columnconfigure(0, weight=1) 245187358Sjeff self.columnconfigure(2, minsize=150) 246139313Sjeff 247139313Sjeff def setcolor(self, color): 248139313Sjeff self.color.set(color) 249139313Sjeff self.sample.itemconfigure(self.rect, fill=color) 250139313Sjeff 251139313Sjeff def apply(self): 252139313Sjeff cchange = 0 253139313Sjeff echange = 0 254139313Sjeff if (self.color_current != self.color.get()): 255139313Sjeff cchange = 1 256139313Sjeff if (self.enabled_current != self.enabled.get()): 257139313Sjeff echange = 1 258139313Sjeff self.color_current = self.color.get() 259139313Sjeff self.enabled_current = self.enabled.get() 260139313Sjeff if (echange != 0): 261139313Sjeff if (self.enabled_current): 262139313Sjeff graph.setcolor(self.name, self.color_current) 263139313Sjeff else: 264139313Sjeff graph.hide(self.name) 265139313Sjeff return 266139313Sjeff if (cchange != 0): 267139313Sjeff graph.setcolor(self.name, self.color_current) 268139313Sjeff 269139313Sjeff def revert(self): 270139313Sjeff self.setcolor(self.color_default) 271139313Sjeff self.enabled.set(self.enabled_default) 272139313Sjeff 273187358Sjeffclass ColorConfigure(Toplevel): 274187358Sjeff def __init__(self, table, name): 275139313Sjeff Toplevel.__init__(self) 276139313Sjeff self.resizable(0, 0) 277187358Sjeff self.title(name) 278187358Sjeff self.items = LabelFrame(self, text="Item Type") 279139313Sjeff self.buttons = Frame(self) 280139313Sjeff self.drawbuttons() 281139313Sjeff self.items.grid(row=0, column=0, sticky=E+W) 282139313Sjeff self.columnconfigure(0, weight=1) 283139313Sjeff self.buttons.grid(row=1, column=0, sticky=E+W) 284139313Sjeff self.types = [] 285139313Sjeff self.irow = 0 286187358Sjeff for type in table: 287187358Sjeff color = graph.getcolor(type[0]) 288187358Sjeff if (color != ""): 289187358Sjeff self.additem(type[0], color) 290278694Ssbruno self.bind("<Control-w>", self.destroycb) 291139313Sjeff 292278694Ssbruno def destroycb(self, event): 293278694Ssbruno self.destroy() 294278694Ssbruno 295187358Sjeff def additem(self, name, color): 296187358Sjeff item = ColorConf(self.items, name, color) 297139313Sjeff self.types.append(item) 298139313Sjeff item.grid(row=self.irow, column=0, sticky=E+W) 299139313Sjeff self.irow += 1 300139313Sjeff 301139313Sjeff def drawbuttons(self): 302139313Sjeff self.apply = Button(self.buttons, text="Apply", 303139313Sjeff command=self.apress) 304187358Sjeff self.default = Button(self.buttons, text="Revert", 305139313Sjeff command=self.rpress) 306139313Sjeff self.apply.grid(row=0, column=0, sticky=E+W) 307187358Sjeff self.default.grid(row=0, column=1, sticky=E+W) 308139313Sjeff self.buttons.columnconfigure(0, weight=1) 309139313Sjeff self.buttons.columnconfigure(1, weight=1) 310139313Sjeff 311139313Sjeff def apress(self): 312139313Sjeff for item in self.types: 313139313Sjeff item.apply() 314139313Sjeff 315139313Sjeff def rpress(self): 316139313Sjeff for item in self.types: 317139313Sjeff item.revert() 318139313Sjeff 319187359Sjeffclass SourceConf(Frame): 320187359Sjeff def __init__(self, master, source): 321187359Sjeff Frame.__init__(self, master) 322187359Sjeff if (source.hidden == 1): 323187359Sjeff enabled = 0 324187359Sjeff else: 325187359Sjeff enabled = 1 326187359Sjeff self.source = source 327187359Sjeff self.name = source.name 328187359Sjeff self.enabled = IntVar() 329187359Sjeff self.enabled_default = enabled 330187359Sjeff self.enabled_current = enabled 331187359Sjeff self.enabled.set(enabled) 332187359Sjeff self.draw() 333187359Sjeff 334187359Sjeff def draw(self): 335187359Sjeff self.label = Label(self, text=self.name, anchor=W) 336187359Sjeff self.checkbox = Checkbutton(self, text="enabled", 337187359Sjeff variable=self.enabled) 338187359Sjeff self.label.grid(row=0, column=0, sticky=E+W) 339187359Sjeff self.checkbox.grid(row=0, column=1) 340187359Sjeff self.columnconfigure(0, weight=1) 341187359Sjeff 342187376Sjeff def changed(self): 343187376Sjeff if (self.enabled_current != self.enabled.get()): 344187376Sjeff return 1 345187376Sjeff return 0 346187376Sjeff 347187359Sjeff def apply(self): 348187359Sjeff self.enabled_current = self.enabled.get() 349187359Sjeff 350187359Sjeff def revert(self): 351187359Sjeff self.enabled.set(self.enabled_default) 352187359Sjeff 353187359Sjeff def check(self): 354187359Sjeff self.enabled.set(1) 355187359Sjeff 356187359Sjeff def uncheck(self): 357187359Sjeff self.enabled.set(0) 358187359Sjeff 359187359Sjeffclass SourceConfigure(Toplevel): 360187359Sjeff def __init__(self): 361187359Sjeff Toplevel.__init__(self) 362187359Sjeff self.resizable(0, 0) 363187359Sjeff self.title("Source Configuration") 364187359Sjeff self.items = [] 365187359Sjeff self.iframe = Frame(self) 366187359Sjeff self.iframe.grid(row=0, column=0, sticky=E+W) 367187359Sjeff f = LabelFrame(self.iframe, bd=4, text="Sources") 368187359Sjeff self.items.append(f) 369187359Sjeff self.buttons = Frame(self) 370187359Sjeff self.items[0].grid(row=0, column=0, sticky=E+W) 371187359Sjeff self.columnconfigure(0, weight=1) 372187359Sjeff self.sconfig = [] 373187359Sjeff self.irow = 0 374187359Sjeff self.icol = 0 375187359Sjeff for source in sources: 376187359Sjeff self.addsource(source) 377187359Sjeff self.drawbuttons() 378187359Sjeff self.buttons.grid(row=1, column=0, sticky=W) 379278694Ssbruno self.bind("<Control-w>", self.destroycb) 380187359Sjeff 381278694Ssbruno def destroycb(self, event): 382278694Ssbruno self.destroy() 383278694Ssbruno 384187359Sjeff def addsource(self, source): 385187359Sjeff if (self.irow > 30): 386187359Sjeff self.icol += 1 387187359Sjeff self.irow = 0 388187359Sjeff c = self.icol 389187359Sjeff f = LabelFrame(self.iframe, bd=4, text="Sources") 390187359Sjeff f.grid(row=0, column=c, sticky=N+E+W) 391187359Sjeff self.items.append(f) 392187359Sjeff item = SourceConf(self.items[self.icol], source) 393187359Sjeff self.sconfig.append(item) 394187359Sjeff item.grid(row=self.irow, column=0, sticky=E+W) 395187359Sjeff self.irow += 1 396187359Sjeff 397187359Sjeff def drawbuttons(self): 398187359Sjeff self.apply = Button(self.buttons, text="Apply", 399187359Sjeff command=self.apress) 400187359Sjeff self.default = Button(self.buttons, text="Revert", 401187359Sjeff command=self.rpress) 402187359Sjeff self.checkall = Button(self.buttons, text="Check All", 403187359Sjeff command=self.cpress) 404187359Sjeff self.uncheckall = Button(self.buttons, text="Uncheck All", 405187359Sjeff command=self.upress) 406187359Sjeff self.checkall.grid(row=0, column=0, sticky=W) 407187359Sjeff self.uncheckall.grid(row=0, column=1, sticky=W) 408187359Sjeff self.apply.grid(row=0, column=2, sticky=W) 409187359Sjeff self.default.grid(row=0, column=3, sticky=W) 410187359Sjeff self.buttons.columnconfigure(0, weight=1) 411187359Sjeff self.buttons.columnconfigure(1, weight=1) 412187359Sjeff self.buttons.columnconfigure(2, weight=1) 413187359Sjeff self.buttons.columnconfigure(3, weight=1) 414187359Sjeff 415187359Sjeff def apress(self): 416187376Sjeff disable_sources = [] 417187376Sjeff enable_sources = [] 418187359Sjeff for item in self.sconfig: 419187376Sjeff if (item.changed() == 0): 420187376Sjeff continue 421187376Sjeff if (item.enabled.get() == 1): 422187376Sjeff enable_sources.append(item.source) 423187376Sjeff else: 424187376Sjeff disable_sources.append(item.source) 425187376Sjeff 426187376Sjeff if (len(disable_sources)): 427187376Sjeff graph.sourcehidelist(disable_sources) 428187376Sjeff if (len(enable_sources)): 429187376Sjeff graph.sourceshowlist(enable_sources) 430187376Sjeff 431187376Sjeff for item in self.sconfig: 432187359Sjeff item.apply() 433187359Sjeff 434187359Sjeff def rpress(self): 435187359Sjeff for item in self.sconfig: 436187359Sjeff item.revert() 437187359Sjeff 438187359Sjeff def cpress(self): 439187359Sjeff for item in self.sconfig: 440187359Sjeff item.check() 441187359Sjeff 442187359Sjeff def upress(self): 443187359Sjeff for item in self.sconfig: 444187359Sjeff item.uncheck() 445187359Sjeff 446187376Sjeff# Reverse compare of second member of the tuple 447187376Sjeffdef cmp_counts(x, y): 448187376Sjeff return y[1] - x[1] 449187376Sjeff 450187376Sjeffclass SourceStats(Toplevel): 451187376Sjeff def __init__(self, source): 452187376Sjeff self.source = source 453187376Sjeff Toplevel.__init__(self) 454187376Sjeff self.resizable(0, 0) 455187376Sjeff self.title(source.name + " statistics") 456187376Sjeff self.evframe = LabelFrame(self, 457187580Sjeff text="Event Count, Duration, Avg Duration") 458187376Sjeff self.evframe.grid(row=0, column=0, sticky=E+W) 459187376Sjeff eventtypes={} 460187376Sjeff for event in self.source.events: 461187376Sjeff if (event.type == "pad"): 462187376Sjeff continue 463187376Sjeff duration = event.duration 464187376Sjeff if (eventtypes.has_key(event.name)): 465187376Sjeff (c, d) = eventtypes[event.name] 466187376Sjeff c += 1 467187376Sjeff d += duration 468187376Sjeff eventtypes[event.name] = (c, d) 469187376Sjeff else: 470187376Sjeff eventtypes[event.name] = (1, duration) 471187376Sjeff events = [] 472187376Sjeff for k, v in eventtypes.iteritems(): 473187376Sjeff (c, d) = v 474187376Sjeff events.append((k, c, d)) 475187376Sjeff events.sort(cmp=cmp_counts) 476187376Sjeff 477187376Sjeff ypos = 0 478187376Sjeff for event in events: 479187376Sjeff (name, c, d) = event 480187580Sjeff Label(self.evframe, text=name, bd=1, 481187580Sjeff relief=SUNKEN, anchor=W, width=30).grid( 482187580Sjeff row=ypos, column=0, sticky=W+E) 483187580Sjeff Label(self.evframe, text=str(c), bd=1, 484187580Sjeff relief=SUNKEN, anchor=W, width=10).grid( 485187580Sjeff row=ypos, column=1, sticky=W+E) 486187580Sjeff Label(self.evframe, text=ticks2sec(d), 487187580Sjeff bd=1, relief=SUNKEN, width=10).grid( 488187580Sjeff row=ypos, column=2, sticky=W+E) 489187580Sjeff if (d and c): 490187580Sjeff d /= c 491187580Sjeff else: 492187580Sjeff d = 0 493187580Sjeff Label(self.evframe, text=ticks2sec(d), 494187580Sjeff bd=1, relief=SUNKEN, width=10).grid( 495187580Sjeff row=ypos, column=3, sticky=W+E) 496187376Sjeff ypos += 1 497278694Ssbruno self.bind("<Control-w>", self.destroycb) 498187376Sjeff 499278694Ssbruno def destroycb(self, event): 500278694Ssbruno self.destroy() 501187376Sjeff 502278694Ssbruno 503187376Sjeffclass SourceContext(Menu): 504187376Sjeff def __init__(self, event, source): 505187376Sjeff self.source = source 506187376Sjeff Menu.__init__(self, tearoff=0, takefocus=0) 507187376Sjeff self.add_command(label="hide", command=self.hide) 508187376Sjeff self.add_command(label="hide group", command=self.hidegroup) 509187376Sjeff self.add_command(label="stats", command=self.stats) 510187376Sjeff self.tk_popup(event.x_root-3, event.y_root+3) 511187376Sjeff 512187376Sjeff def hide(self): 513187376Sjeff graph.sourcehide(self.source) 514187376Sjeff 515187376Sjeff def hidegroup(self): 516187376Sjeff grouplist = [] 517187376Sjeff for source in sources: 518187376Sjeff if (source.group == self.source.group): 519187376Sjeff grouplist.append(source) 520187376Sjeff graph.sourcehidelist(grouplist) 521187376Sjeff 522187376Sjeff def show(self): 523187376Sjeff graph.sourceshow(self.source) 524187376Sjeff 525187376Sjeff def stats(self): 526187376Sjeff SourceStats(self.source) 527187376Sjeff 528139313Sjeffclass EventView(Toplevel): 529139313Sjeff def __init__(self, event, canvas): 530139313Sjeff Toplevel.__init__(self) 531139313Sjeff self.resizable(0, 0) 532139313Sjeff self.title("Event") 533139313Sjeff self.event = event 534187358Sjeff self.buttons = Frame(self) 535187358Sjeff self.buttons.grid(row=0, column=0, sticky=E+W) 536139313Sjeff self.frame = Frame(self) 537187358Sjeff self.frame.grid(row=1, column=0, sticky=N+S+E+W) 538139313Sjeff self.canvas = canvas 539139313Sjeff self.drawlabels() 540139313Sjeff self.drawbuttons() 541139313Sjeff event.displayref(canvas) 542139313Sjeff self.bind("<Destroy>", self.destroycb) 543278694Ssbruno self.bind("<Control-w>", self.destroycb) 544139313Sjeff 545139313Sjeff def destroycb(self, event): 546139313Sjeff self.unbind("<Destroy>") 547139313Sjeff if (self.event != None): 548139313Sjeff self.event.displayunref(self.canvas) 549139313Sjeff self.event = None 550139313Sjeff self.destroy() 551139313Sjeff 552139313Sjeff def clearlabels(self): 553139313Sjeff for label in self.frame.grid_slaves(): 554139313Sjeff label.grid_remove() 555139313Sjeff 556139313Sjeff def drawlabels(self): 557139313Sjeff ypos = 0 558139313Sjeff labels = self.event.labels() 559139313Sjeff while (len(labels) < 7): 560187358Sjeff labels.append(("", "")) 561139313Sjeff for label in labels: 562187358Sjeff name, value = label 563187358Sjeff linked = 0 564187358Sjeff if (name == "linkedto"): 565187358Sjeff linked = 1 566139313Sjeff l = Label(self.frame, text=name, bd=1, width=15, 567139313Sjeff relief=SUNKEN, anchor=W) 568139313Sjeff if (linked): 569139313Sjeff fgcolor = "blue" 570139313Sjeff else: 571139313Sjeff fgcolor = "black" 572139313Sjeff r = Label(self.frame, text=value, bd=1, 573139313Sjeff relief=SUNKEN, anchor=W, fg=fgcolor) 574139313Sjeff l.grid(row=ypos, column=0, sticky=E+W) 575139313Sjeff r.grid(row=ypos, column=1, sticky=E+W) 576139313Sjeff if (linked): 577139313Sjeff r.bind("<Button-1>", self.linkpress) 578139313Sjeff ypos += 1 579139313Sjeff self.frame.columnconfigure(1, minsize=80) 580139313Sjeff 581139313Sjeff def drawbuttons(self): 582139313Sjeff self.back = Button(self.buttons, text="<", command=self.bpress) 583139313Sjeff self.forw = Button(self.buttons, text=">", command=self.fpress) 584139313Sjeff self.new = Button(self.buttons, text="new", command=self.npress) 585139313Sjeff self.back.grid(row=0, column=0, sticky=E+W) 586139313Sjeff self.forw.grid(row=0, column=1, sticky=E+W) 587139313Sjeff self.new.grid(row=0, column=2, sticky=E+W) 588139313Sjeff self.buttons.columnconfigure(2, weight=1) 589139313Sjeff 590139313Sjeff def newevent(self, event): 591139313Sjeff self.event.displayunref(self.canvas) 592139313Sjeff self.clearlabels() 593139313Sjeff self.event = event 594139313Sjeff self.event.displayref(self.canvas) 595139313Sjeff self.drawlabels() 596139313Sjeff 597139313Sjeff def npress(self): 598139313Sjeff EventView(self.event, self.canvas) 599139313Sjeff 600139313Sjeff def bpress(self): 601139313Sjeff prev = self.event.prev() 602139313Sjeff if (prev == None): 603139313Sjeff return 604187358Sjeff while (prev.type == "pad"): 605139313Sjeff prev = prev.prev() 606139313Sjeff if (prev == None): 607139313Sjeff return 608139313Sjeff self.newevent(prev) 609139313Sjeff 610139313Sjeff def fpress(self): 611139313Sjeff next = self.event.next() 612139313Sjeff if (next == None): 613139313Sjeff return 614187358Sjeff while (next.type == "pad"): 615139313Sjeff next = next.next() 616139313Sjeff if (next == None): 617139313Sjeff return 618139313Sjeff self.newevent(next) 619139313Sjeff 620139313Sjeff def linkpress(self, wevent): 621139313Sjeff event = self.event.getlinked() 622139313Sjeff if (event != None): 623139313Sjeff self.newevent(event) 624139313Sjeff 625139313Sjeffclass Event: 626187358Sjeff def __init__(self, source, name, cpu, timestamp, attrs): 627139313Sjeff self.source = source 628187358Sjeff self.name = name 629139313Sjeff self.cpu = cpu 630139313Sjeff self.timestamp = int(timestamp) 631187358Sjeff self.attrs = attrs 632139313Sjeff self.idx = None 633139313Sjeff self.item = None 634139313Sjeff self.dispcnt = 0 635187376Sjeff self.duration = 0 636173743Ssam self.recno = lineno 637139313Sjeff 638139313Sjeff def status(self): 639139313Sjeff statstr = self.name + " " + self.source.name 640139313Sjeff statstr += " on: cpu" + str(self.cpu) 641139313Sjeff statstr += " at: " + str(self.timestamp) 642187358Sjeff statstr += " attributes: " 643187358Sjeff for i in range(0, len(self.attrs)): 644187358Sjeff attr = self.attrs[i] 645187358Sjeff statstr += attr[0] + ": " + str(attr[1]) 646187358Sjeff if (i != len(self.attrs) - 1): 647187358Sjeff statstr += ", " 648139313Sjeff status.set(statstr) 649139313Sjeff 650187358Sjeff def labels(self): 651187358Sjeff return [("Source", self.source.name), 652187358Sjeff ("Event", self.name), 653187358Sjeff ("CPU", self.cpu), 654187358Sjeff ("Timestamp", self.timestamp), 655187358Sjeff ("KTR Line ", self.recno) 656187358Sjeff ] + self.attrs 657139313Sjeff 658187358Sjeff def mouseenter(self, canvas): 659139313Sjeff self.displayref(canvas) 660139313Sjeff self.status() 661139313Sjeff 662187358Sjeff def mouseexit(self, canvas): 663139313Sjeff self.displayunref(canvas) 664139313Sjeff status.clear() 665139313Sjeff 666187358Sjeff def mousepress(self, canvas): 667139313Sjeff EventView(self, canvas) 668139313Sjeff 669187358Sjeff def draw(self, canvas, xpos, ypos, item): 670187358Sjeff self.item = item 671187358Sjeff if (item != None): 672187358Sjeff canvas.items[item] = self 673187358Sjeff 674187358Sjeff def move(self, canvas, x, y): 675187358Sjeff if (self.item == None): 676187358Sjeff return; 677187358Sjeff canvas.move(self.item, x, y); 678187358Sjeff 679139313Sjeff def next(self): 680139313Sjeff return self.source.eventat(self.idx + 1) 681139313Sjeff 682187358Sjeff def nexttype(self, type): 683187358Sjeff next = self.next() 684187358Sjeff while (next != None and next.type != type): 685187358Sjeff next = next.next() 686187358Sjeff return (next) 687187358Sjeff 688139313Sjeff def prev(self): 689139313Sjeff return self.source.eventat(self.idx - 1) 690139313Sjeff 691139313Sjeff def displayref(self, canvas): 692139313Sjeff if (self.dispcnt == 0): 693139313Sjeff canvas.itemconfigure(self.item, width=2) 694139313Sjeff self.dispcnt += 1 695139313Sjeff 696139313Sjeff def displayunref(self, canvas): 697139313Sjeff self.dispcnt -= 1 698139313Sjeff if (self.dispcnt == 0): 699139313Sjeff canvas.itemconfigure(self.item, width=0) 700139313Sjeff canvas.tag_raise("point", "state") 701139313Sjeff 702139313Sjeff def getlinked(self): 703187358Sjeff for attr in self.attrs: 704187358Sjeff if (attr[0] != "linkedto"): 705187358Sjeff continue 706187358Sjeff source = ktrfile.findid(attr[1]) 707187358Sjeff return source.findevent(self.timestamp) 708187358Sjeff return None 709139313Sjeff 710139313Sjeffclass PointEvent(Event): 711187358Sjeff type = "point" 712187358Sjeff def __init__(self, source, name, cpu, timestamp, attrs): 713187358Sjeff Event.__init__(self, source, name, cpu, timestamp, attrs) 714139313Sjeff 715139313Sjeff def draw(self, canvas, xpos, ypos): 716187358Sjeff color = colormap.lookup(self.name) 717187379Sjeff l = canvas.create_oval(xpos - XY_POINT, ypos, 718187379Sjeff xpos + XY_POINT, ypos - (XY_POINT * 2), 719187376Sjeff fill=color, width=0, 720187379Sjeff tags=("event", self.type, self.name, self.source.tag)) 721187358Sjeff Event.draw(self, canvas, xpos, ypos, l) 722139313Sjeff 723187358Sjeff return xpos 724139313Sjeff 725139313Sjeffclass StateEvent(Event): 726187358Sjeff type = "state" 727187358Sjeff def __init__(self, source, name, cpu, timestamp, attrs): 728187358Sjeff Event.__init__(self, source, name, cpu, timestamp, attrs) 729139313Sjeff 730139313Sjeff def draw(self, canvas, xpos, ypos): 731187358Sjeff next = self.nexttype("state") 732187358Sjeff if (next == None): 733139313Sjeff return (xpos) 734187376Sjeff self.duration = duration = next.timestamp - self.timestamp 735187358Sjeff self.attrs.insert(0, ("duration", ticks2sec(duration))) 736187358Sjeff color = colormap.lookup(self.name) 737187358Sjeff if (duration < 0): 738187358Sjeff duration = 0 739166209Sjeff print "Unsynchronized timestamp" 740166209Sjeff print self.cpu, self.timestamp 741166209Sjeff print next.cpu, next.timestamp 742187358Sjeff delta = duration / canvas.ratio 743139313Sjeff l = canvas.create_rectangle(xpos, ypos, 744187358Sjeff xpos + delta, ypos - 10, fill=color, width=0, 745187379Sjeff tags=("event", self.type, self.name, self.source.tag)) 746187358Sjeff Event.draw(self, canvas, xpos, ypos, l) 747139313Sjeff 748139313Sjeff return (xpos + delta) 749139313Sjeff 750187358Sjeffclass CountEvent(Event): 751187358Sjeff type = "count" 752187358Sjeff def __init__(self, source, count, cpu, timestamp, attrs): 753187358Sjeff count = int(count) 754187358Sjeff self.count = count 755187358Sjeff Event.__init__(self, source, "count", cpu, timestamp, attrs) 756139313Sjeff 757139313Sjeff def draw(self, canvas, xpos, ypos): 758187358Sjeff next = self.nexttype("count") 759187358Sjeff if (next == None): 760187358Sjeff return (xpos) 761187358Sjeff color = colormap.lookup("count") 762187376Sjeff self.duration = duration = next.timestamp - self.timestamp 763187471Sjeff if (duration < 0): 764187471Sjeff duration = 0 765187471Sjeff print "Unsynchronized timestamp" 766187471Sjeff print self.cpu, self.timestamp 767187471Sjeff print next.cpu, next.timestamp 768187358Sjeff self.attrs.insert(0, ("count", self.count)) 769187358Sjeff self.attrs.insert(1, ("duration", ticks2sec(duration))) 770187358Sjeff delta = duration / canvas.ratio 771139313Sjeff yhight = self.source.yscale() * self.count 772139313Sjeff l = canvas.create_rectangle(xpos, ypos - yhight, 773187358Sjeff xpos + delta, ypos, fill=color, width=0, 774187379Sjeff tags=("event", self.type, self.name, self.source.tag)) 775187358Sjeff Event.draw(self, canvas, xpos, ypos, l) 776139313Sjeff return (xpos + delta) 777139313Sjeff 778187358Sjeffclass PadEvent(StateEvent): 779187358Sjeff type = "pad" 780187358Sjeff def __init__(self, source, cpu, timestamp, last=0): 781187358Sjeff if (last): 782187358Sjeff cpu = source.events[len(source.events) -1].cpu 783187358Sjeff else: 784187358Sjeff cpu = source.events[0].cpu 785187358Sjeff StateEvent.__init__(self, source, "pad", cpu, timestamp, []) 786139313Sjeff def draw(self, canvas, xpos, ypos): 787139313Sjeff next = self.next() 788139313Sjeff if (next == None): 789139313Sjeff return (xpos) 790187358Sjeff duration = next.timestamp - self.timestamp 791187358Sjeff delta = duration / canvas.ratio 792187358Sjeff Event.draw(self, canvas, xpos, ypos, None) 793139313Sjeff return (xpos + delta) 794139313Sjeff 795187376Sjeff# Sort function for start y address 796187376Sjeffdef source_cmp_start(x, y): 797187376Sjeff return x.y - y.y 798187376Sjeff 799139313Sjeffclass EventSource: 800187358Sjeff def __init__(self, group, id): 801187358Sjeff self.name = id 802139313Sjeff self.events = [] 803187358Sjeff self.cpuitems = [] 804187155Sjhb self.group = group 805187358Sjeff self.y = 0 806187358Sjeff self.item = None 807187359Sjeff self.hidden = 0 808187376Sjeff self.tag = group + id 809139313Sjeff 810187155Sjhb def __cmp__(self, other): 811187358Sjeff if (other == None): 812187358Sjeff return -1 813187155Sjhb if (self.group == other.group): 814187358Sjeff return cmp(self.name, other.name) 815187155Sjhb return cmp(self.group, other.group) 816187155Sjhb 817187155Sjhb # It is much faster to append items to a list then to insert them 818187155Sjhb # at the beginning. As a result, we add events in reverse order 819187155Sjhb # and then swap the list during fixup. 820139313Sjeff def fixup(self): 821187155Sjhb self.events.reverse() 822139313Sjeff 823187358Sjeff def addevent(self, event): 824187155Sjhb self.events.append(event) 825139313Sjeff 826187358Sjeff def addlastevent(self, event): 827187155Sjhb self.events.insert(0, event) 828139313Sjeff 829139313Sjeff def draw(self, canvas, ypos): 830139313Sjeff xpos = 10 831187358Sjeff cpux = 10 832187358Sjeff cpu = self.events[1].cpu 833139313Sjeff for i in range(0, len(self.events)): 834139313Sjeff self.events[i].idx = i 835139313Sjeff for event in self.events: 836187358Sjeff if (event.cpu != cpu and event.cpu != -1): 837187358Sjeff self.drawcpu(canvas, cpu, cpux, xpos, ypos) 838187358Sjeff cpux = xpos 839187358Sjeff cpu = event.cpu 840139313Sjeff xpos = event.draw(canvas, xpos, ypos) 841187358Sjeff self.drawcpu(canvas, cpu, cpux, xpos, ypos) 842139313Sjeff 843139313Sjeff def drawname(self, canvas, ypos): 844187358Sjeff self.y = ypos 845139313Sjeff ypos = ypos - (self.ysize() / 2) 846187379Sjeff self.item = canvas.create_text(X_BORDER, ypos, anchor="w", 847187379Sjeff text=self.name) 848187358Sjeff return (self.item) 849139313Sjeff 850187358Sjeff def drawcpu(self, canvas, cpu, fromx, tox, ypos): 851187358Sjeff cpu = "CPU " + str(cpu) 852187358Sjeff color = cpucolormap.lookup(cpu) 853187358Sjeff # Create the cpu background colors default to hidden 854187358Sjeff l = canvas.create_rectangle(fromx, 855139313Sjeff ypos - self.ysize() - canvas.bdheight, 856187358Sjeff tox, ypos + canvas.bdheight, fill=color, width=0, 857187379Sjeff tags=("cpubg", cpu, self.tag), state="hidden") 858187358Sjeff self.cpuitems.append(l) 859139313Sjeff 860187358Sjeff def move(self, canvas, xpos, ypos): 861187376Sjeff canvas.move(self.tag, xpos, ypos) 862187358Sjeff 863187358Sjeff def movename(self, canvas, xpos, ypos): 864187358Sjeff self.y += ypos 865187358Sjeff canvas.move(self.item, xpos, ypos) 866187358Sjeff 867139313Sjeff def ysize(self): 868187379Sjeff return (Y_EVENTSOURCE) 869139313Sjeff 870139313Sjeff def eventat(self, i): 871278694Ssbruno if (i >= len(self.events) or i < 0): 872139313Sjeff return (None) 873139313Sjeff event = self.events[i] 874139313Sjeff return (event) 875139313Sjeff 876139313Sjeff def findevent(self, timestamp): 877139313Sjeff for event in self.events: 878187358Sjeff if (event.timestamp >= timestamp and event.type != "pad"): 879139313Sjeff return (event) 880139313Sjeff return (None) 881139313Sjeff 882187358Sjeffclass Counter(EventSource): 883187358Sjeff # 884187358Sjeff # Store a hash of counter groups that keeps the max value 885187358Sjeff # for a counter in this group for scaling purposes. 886187358Sjeff # 887187358Sjeff groups = {} 888187358Sjeff def __init__(self, group, id): 889139313Sjeff try: 890187358Sjeff Counter.cnt = Counter.groups[group] 891139313Sjeff except: 892187358Sjeff Counter.groups[group] = 0 893187358Sjeff EventSource.__init__(self, group, id) 894139313Sjeff 895139313Sjeff def fixup(self): 896187358Sjeff for event in self.events: 897187358Sjeff if (event.type != "count"): 898187358Sjeff continue; 899187358Sjeff count = int(event.count) 900187358Sjeff if (count > Counter.groups[self.group]): 901187358Sjeff Counter.groups[self.group] = count 902187155Sjhb EventSource.fixup(self) 903139313Sjeff 904173743Ssam def ymax(self): 905187358Sjeff return (Counter.groups[self.group]) 906173743Ssam 907139313Sjeff def ysize(self): 908187379Sjeff return (Y_COUNTER) 909139313Sjeff 910139313Sjeff def yscale(self): 911187358Sjeff return (self.ysize() / self.ymax()) 912139313Sjeff 913139313Sjeffclass KTRFile: 914139313Sjeff def __init__(self, file): 915166209Sjeff self.timestamp_f = None 916166209Sjeff self.timestamp_l = None 917187156Sjhb self.locks = {} 918139313Sjeff self.ticks = {} 919139313Sjeff self.load = {} 920152663Sscottl self.crit = {} 921166209Sjeff self.stathz = 0 922187379Sjeff self.eventcnt = 0 923187471Sjeff self.taghash = {} 924139313Sjeff 925139313Sjeff self.parse(file) 926139313Sjeff self.fixup() 927139313Sjeff global ticksps 928139313Sjeff ticksps = self.ticksps() 929187379Sjeff span = self.timespan() 930187379Sjeff ghz = float(ticksps) / 1000000000.0 931187379Sjeff # 932187379Sjeff # Update the title with some stats from the file 933187379Sjeff # 934187379Sjeff titlestr = "SchedGraph: " 935187379Sjeff titlestr += ticks2sec(span) + " at %.3f ghz, " % ghz 936187379Sjeff titlestr += str(len(sources)) + " event sources, " 937187379Sjeff titlestr += str(self.eventcnt) + " events" 938187379Sjeff root.title(titlestr) 939139313Sjeff 940139313Sjeff def parse(self, file): 941139313Sjeff try: 942139313Sjeff ifp = open(file) 943139313Sjeff except: 944139313Sjeff print "Can't open", file 945139313Sjeff sys.exit(1) 946139313Sjeff 947187358Sjeff # quoteexp matches a quoted string, no escaping 948187358Sjeff quoteexp = "\"([^\"]*)\"" 949139313Sjeff 950187358Sjeff # 951187358Sjeff # commaexp matches a quoted string OR the string up 952187358Sjeff # to the first ',' 953187358Sjeff # 954187358Sjeff commaexp = "(?:" + quoteexp + "|([^,]+))" 955139313Sjeff 956187358Sjeff # 957187358Sjeff # colonstr matches a quoted string OR the string up 958187358Sjeff # to the first ':' 959187358Sjeff # 960187358Sjeff colonexp = "(?:" + quoteexp + "|([^:]+))" 961139313Sjeff 962187358Sjeff # 963187358Sjeff # Match various manditory parts of the KTR string this is 964187358Sjeff # fairly inflexible until you get to attributes to make 965187358Sjeff # parsing faster. 966187358Sjeff # 967187358Sjeff hdrexp = "\s*(\d+)\s+(\d+)\s+(\d+)\s+" 968187358Sjeff groupexp = "KTRGRAPH group:" + quoteexp + ", " 969187358Sjeff idexp = "id:" + quoteexp + ", " 970187358Sjeff typeexp = "([^:]+):" + commaexp + ", " 971187358Sjeff attribexp = "attributes: (.*)" 972139313Sjeff 973187358Sjeff # 974187358Sjeff # Matches optional attributes in the KTR string. This 975187358Sjeff # tolerates more variance as the users supply these values. 976187358Sjeff # 977187358Sjeff attrexp = colonexp + "\s*:\s*(?:" + commaexp + ", (.*)|" 978187358Sjeff attrexp += quoteexp +"|(.*))" 979139313Sjeff 980187358Sjeff # Precompile regexp 981187358Sjeff ktrre = re.compile(hdrexp + groupexp + idexp + typeexp + attribexp) 982187358Sjeff attrre = re.compile(attrexp) 983139313Sjeff 984173743Ssam global lineno 985173743Ssam lineno = 0 986178571Sjeff for line in ifp.readlines(): 987173743Ssam lineno += 1 988187358Sjeff if ((lineno % 2048) == 0): 989173743Ssam status.startup("Parsing line " + str(lineno)) 990187358Sjeff m = ktrre.match(line); 991173568Sjeff if (m == None): 992187358Sjeff print "Can't parse", lineno, line, 993187358Sjeff continue; 994187358Sjeff (index, cpu, timestamp, group, id, type, dat, dat1, attrstring) = m.groups(); 995187358Sjeff if (dat == None): 996187358Sjeff dat = dat1 997187358Sjeff if (self.checkstamp(timestamp) == 0): 998187471Sjeff print "Bad timestamp at", lineno, ":", 999187471Sjeff print cpu, timestamp 1000187358Sjeff continue 1001187358Sjeff # 1002187358Sjeff # Build the table of optional attributes 1003187358Sjeff # 1004187358Sjeff attrs = [] 1005187358Sjeff while (attrstring != None): 1006187358Sjeff m = attrre.match(attrstring.strip()) 1007187358Sjeff if (m == None): 1008187358Sjeff break; 1009187358Sjeff # 1010187358Sjeff # Name may or may not be quoted. 1011187358Sjeff # 1012187358Sjeff # For val we have four cases: 1013187358Sjeff # 1) quotes followed by comma and more 1014187358Sjeff # attributes. 1015187358Sjeff # 2) no quotes followed by comma and more 1016187358Sjeff # attributes. 1017187358Sjeff # 3) no more attributes or comma with quotes. 1018187358Sjeff # 4) no more attributes or comma without quotes. 1019187358Sjeff # 1020187358Sjeff (name, name1, val, val1, attrstring, end, end1) = m.groups(); 1021187358Sjeff if (name == None): 1022187358Sjeff name = name1 1023187358Sjeff if (end == None): 1024187358Sjeff end = end1 1025187358Sjeff if (val == None): 1026187358Sjeff val = val1 1027187358Sjeff if (val == None): 1028187358Sjeff val = end 1029187358Sjeff if (name == "stathz"): 1030187358Sjeff self.setstathz(val, cpu) 1031187358Sjeff attrs.append((name, val)) 1032187358Sjeff args = (dat, cpu, timestamp, attrs) 1033187358Sjeff e = self.makeevent(group, id, type, args) 1034187358Sjeff if (e == None): 1035187358Sjeff print "Unknown type", type, lineno, line, 1036139313Sjeff 1037187358Sjeff def makeevent(self, group, id, type, args): 1038187358Sjeff e = None 1039187358Sjeff source = self.makeid(group, id, type) 1040187358Sjeff if (type == "state"): 1041187358Sjeff e = StateEvent(source, *args) 1042187358Sjeff elif (type == "counter"): 1043187358Sjeff e = CountEvent(source, *args) 1044187358Sjeff elif (type == "point"): 1045187358Sjeff e = PointEvent(source, *args) 1046187358Sjeff if (e != None): 1047187379Sjeff self.eventcnt += 1 1048187358Sjeff source.addevent(e); 1049187358Sjeff return e 1050139313Sjeff 1051187358Sjeff def setstathz(self, val, cpu): 1052187358Sjeff self.stathz = int(val) 1053139313Sjeff cpu = int(cpu) 1054139313Sjeff try: 1055139313Sjeff ticks = self.ticks[cpu] 1056139313Sjeff except: 1057139313Sjeff self.ticks[cpu] = 0 1058139313Sjeff self.ticks[cpu] += 1 1059139313Sjeff 1060187358Sjeff def checkstamp(self, timestamp): 1061187358Sjeff timestamp = int(timestamp) 1062187358Sjeff if (self.timestamp_f == None): 1063187358Sjeff self.timestamp_f = timestamp; 1064187471Sjeff if (self.timestamp_l != None and 1065187471Sjeff timestamp -2048> self.timestamp_l): 1066187358Sjeff return (0) 1067187358Sjeff self.timestamp_l = timestamp; 1068187358Sjeff return (1) 1069139313Sjeff 1070187358Sjeff def makeid(self, group, id, type): 1071187471Sjeff tag = group + id 1072187471Sjeff if (self.taghash.has_key(tag)): 1073187471Sjeff return self.taghash[tag] 1074187358Sjeff if (type == "counter"): 1075187358Sjeff source = Counter(group, id) 1076187358Sjeff else: 1077187358Sjeff source = EventSource(group, id) 1078187358Sjeff sources.append(source) 1079187471Sjeff self.taghash[tag] = source 1080187358Sjeff return (source) 1081139313Sjeff 1082187358Sjeff def findid(self, id): 1083187358Sjeff for source in sources: 1084187358Sjeff if (source.name == id): 1085187358Sjeff return source 1086187358Sjeff return (None) 1087173568Sjeff 1088187358Sjeff def timespan(self): 1089187358Sjeff return (self.timestamp_f - self.timestamp_l); 1090139313Sjeff 1091187358Sjeff def ticksps(self): 1092187358Sjeff oneghz = 1000000000 1093187358Sjeff # Use user supplied clock first 1094187358Sjeff if (clockfreq != None): 1095187358Sjeff return int(clockfreq * oneghz) 1096152663Sscottl 1097187358Sjeff # Check for a discovered clock 1098187471Sjeff if (self.stathz != 0): 1099187358Sjeff return (self.timespan() / self.ticks[0]) * int(self.stathz) 1100187358Sjeff # Pretend we have a 1ns clock 1101187358Sjeff print "WARNING: No clock discovered and no frequency ", 1102187358Sjeff print "specified via the command line." 1103187358Sjeff print "Using fake 1ghz clock" 1104187358Sjeff return (oneghz); 1105187156Sjhb 1106187358Sjeff def fixup(self): 1107187358Sjeff for source in sources: 1108187358Sjeff e = PadEvent(source, -1, self.timestamp_l) 1109187358Sjeff source.addevent(e) 1110187358Sjeff e = PadEvent(source, -1, self.timestamp_f, last=1) 1111187358Sjeff source.addlastevent(e) 1112187358Sjeff source.fixup() 1113187358Sjeff sources.sort() 1114187156Sjhb 1115187358Sjeffclass SchedNames(Canvas): 1116187358Sjeff def __init__(self, master, display): 1117187358Sjeff self.display = display 1118187358Sjeff self.parent = master 1119187358Sjeff self.bdheight = master.bdheight 1120187358Sjeff self.items = {} 1121187358Sjeff self.ysize = 0 1122187358Sjeff self.lines = [] 1123187358Sjeff Canvas.__init__(self, master, width=120, 1124187358Sjeff height=display["height"], bg='grey', 1125187358Sjeff scrollregion=(0, 0, 50, 100)) 1126187156Sjhb 1127187358Sjeff def moveline(self, cur_y, y): 1128187358Sjeff for line in self.lines: 1129187358Sjeff (x0, y0, x1, y1) = self.coords(line) 1130187358Sjeff if (cur_y != y0): 1131187358Sjeff continue 1132187358Sjeff self.move(line, 0, y) 1133187156Sjhb return 1134187156Sjhb 1135187358Sjeff def draw(self): 1136187358Sjeff status.startup("Drawing names") 1137187358Sjeff ypos = 0 1138187358Sjeff self.configure(scrollregion=(0, 0, 1139187358Sjeff self["width"], self.display.ysize())) 1140187358Sjeff for source in sources: 1141187358Sjeff l = self.create_line(0, ypos, self["width"], ypos, 1142187358Sjeff width=1, fill="black", tags=("all","sources")) 1143187358Sjeff self.lines.append(l) 1144187358Sjeff ypos += self.bdheight 1145187358Sjeff ypos += source.ysize() 1146187358Sjeff t = source.drawname(self, ypos) 1147187358Sjeff self.items[t] = source 1148187358Sjeff ypos += self.bdheight 1149187358Sjeff self.ysize = ypos 1150187358Sjeff self.create_line(0, ypos, self["width"], ypos, 1151187358Sjeff width=1, fill="black", tags=("all",)) 1152187358Sjeff self.bind("<Button-1>", self.master.mousepress); 1153187376Sjeff self.bind("<Button-3>", self.master.mousepressright); 1154187358Sjeff self.bind("<ButtonRelease-1>", self.master.mouserelease); 1155187358Sjeff self.bind("<B1-Motion>", self.master.mousemotion); 1156187156Sjhb 1157187359Sjeff def updatescroll(self): 1158187359Sjeff self.configure(scrollregion=(0, 0, 1159187359Sjeff self["width"], self.display.ysize())) 1160187156Sjhb 1161187359Sjeff 1162139313Sjeffclass SchedDisplay(Canvas): 1163139313Sjeff def __init__(self, master): 1164187155Sjhb self.ratio = 1 1165187155Sjhb self.parent = master 1166187358Sjeff self.bdheight = master.bdheight 1167187358Sjeff self.items = {} 1168187358Sjeff self.lines = [] 1169139313Sjeff Canvas.__init__(self, master, width=800, height=500, bg='grey', 1170139313Sjeff scrollregion=(0, 0, 800, 500)) 1171139313Sjeff 1172187358Sjeff def prepare(self): 1173187358Sjeff # 1174187155Sjhb # Compute a ratio to ensure that the file's timespan fits into 1175187155Sjhb # 2^31. Although python may handle larger values for X 1176187155Sjhb # values, the Tk internals do not. 1177187358Sjeff # 1178187155Sjhb self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1 1179187155Sjhb 1180139313Sjeff def draw(self): 1181139313Sjeff ypos = 0 1182139313Sjeff xsize = self.xsize() 1183187358Sjeff for source in sources: 1184139313Sjeff status.startup("Drawing " + source.name) 1185187358Sjeff l = self.create_line(0, ypos, xsize, ypos, 1186139313Sjeff width=1, fill="black", tags=("all",)) 1187187358Sjeff self.lines.append(l) 1188139313Sjeff ypos += self.bdheight 1189139313Sjeff ypos += source.ysize() 1190139313Sjeff source.draw(self, ypos) 1191139313Sjeff ypos += self.bdheight 1192187379Sjeff self.tag_raise("point", "state") 1193187379Sjeff self.tag_lower("cpubg", ALL) 1194139313Sjeff self.create_line(0, ypos, xsize, ypos, 1195187379Sjeff width=1, fill="black", tags=("lines",)) 1196139313Sjeff self.tag_bind("event", "<Enter>", self.mouseenter) 1197139313Sjeff self.tag_bind("event", "<Leave>", self.mouseexit) 1198187358Sjeff self.bind("<Button-1>", self.mousepress) 1199187376Sjeff self.bind("<Button-3>", self.master.mousepressright); 1200187155Sjhb self.bind("<Button-4>", self.wheelup) 1201187155Sjhb self.bind("<Button-5>", self.wheeldown) 1202187358Sjeff self.bind("<ButtonRelease-1>", self.master.mouserelease); 1203187358Sjeff self.bind("<B1-Motion>", self.master.mousemotion); 1204139313Sjeff 1205187358Sjeff def moveline(self, cur_y, y): 1206187358Sjeff for line in self.lines: 1207187358Sjeff (x0, y0, x1, y1) = self.coords(line) 1208187358Sjeff if (cur_y != y0): 1209187358Sjeff continue 1210187358Sjeff self.move(line, 0, y) 1211187358Sjeff return 1212187358Sjeff 1213139313Sjeff def mouseenter(self, event): 1214139313Sjeff item, = self.find_withtag(CURRENT) 1215187358Sjeff self.items[item].mouseenter(self) 1216139313Sjeff 1217139313Sjeff def mouseexit(self, event): 1218139313Sjeff item, = self.find_withtag(CURRENT) 1219187358Sjeff self.items[item].mouseexit(self) 1220139313Sjeff 1221139313Sjeff def mousepress(self, event): 1222187358Sjeff # Find out what's beneath us 1223187358Sjeff items = self.find_withtag(CURRENT) 1224187358Sjeff if (len(items) == 0): 1225187358Sjeff self.master.mousepress(event) 1226187358Sjeff return 1227187358Sjeff # Only grab mouse presses for things with event tags. 1228187358Sjeff item = items[0] 1229187358Sjeff tags = self.gettags(item) 1230187358Sjeff for tag in tags: 1231187358Sjeff if (tag == "event"): 1232187358Sjeff self.items[item].mousepress(self) 1233187358Sjeff return 1234187358Sjeff # Leave the rest to the master window 1235187358Sjeff self.master.mousepress(event) 1236139313Sjeff 1237187155Sjhb def wheeldown(self, event): 1238187155Sjhb self.parent.display_yview("scroll", 1, "units") 1239187155Sjhb 1240187155Sjhb def wheelup(self, event): 1241187155Sjhb self.parent.display_yview("scroll", -1, "units") 1242187155Sjhb 1243139313Sjeff def xsize(self): 1244187379Sjeff return ((ktrfile.timespan() / self.ratio) + (X_BORDER * 2)) 1245139313Sjeff 1246139313Sjeff def ysize(self): 1247139313Sjeff ysize = 0 1248187358Sjeff for source in sources: 1249187359Sjeff if (source.hidden == 1): 1250187359Sjeff continue 1251187359Sjeff ysize += self.parent.sourcesize(source) 1252187358Sjeff return ysize 1253139313Sjeff 1254139313Sjeff def scaleset(self, ratio): 1255187358Sjeff if (ktrfile == None): 1256139313Sjeff return 1257139313Sjeff oldratio = self.ratio 1258187358Sjeff xstart, xend = self.xview() 1259187358Sjeff midpoint = xstart + ((xend - xstart) / 2) 1260139313Sjeff 1261139313Sjeff self.ratio = ratio 1262187359Sjeff self.updatescroll() 1263187379Sjeff self.scale(ALL, 0, 0, float(oldratio) / ratio, 1) 1264139313Sjeff 1265187358Sjeff xstart, xend = self.xview() 1266187358Sjeff xsize = (xend - xstart) / 2 1267187358Sjeff self.xview_moveto(midpoint - xsize) 1268139313Sjeff 1269187359Sjeff def updatescroll(self): 1270187359Sjeff self.configure(scrollregion=(0, 0, self.xsize(), self.ysize())) 1271187359Sjeff 1272139313Sjeff def scaleget(self): 1273139313Sjeff return self.ratio 1274139313Sjeff 1275187358Sjeff def getcolor(self, tag): 1276187358Sjeff return self.itemcget(tag, "fill") 1277187358Sjeff 1278187358Sjeff def getstate(self, tag): 1279187358Sjeff return self.itemcget(tag, "state") 1280187358Sjeff 1281139313Sjeff def setcolor(self, tag, color): 1282139313Sjeff self.itemconfigure(tag, state="normal", fill=color) 1283139313Sjeff 1284139313Sjeff def hide(self, tag): 1285139313Sjeff self.itemconfigure(tag, state="hidden") 1286139313Sjeff 1287139313Sjeffclass GraphMenu(Frame): 1288139313Sjeff def __init__(self, master): 1289139313Sjeff Frame.__init__(self, master, bd=2, relief=RAISED) 1290187376Sjeff self.conf = Menubutton(self, text="Configure") 1291187376Sjeff self.confmenu = Menu(self.conf, tearoff=0) 1292187376Sjeff self.confmenu.add_command(label="Event Colors", 1293139313Sjeff command=self.econf) 1294187376Sjeff self.confmenu.add_command(label="CPU Colors", 1295187358Sjeff command=self.cconf) 1296187376Sjeff self.confmenu.add_command(label="Source Configure", 1297187359Sjeff command=self.sconf) 1298187376Sjeff self.conf["menu"] = self.confmenu 1299187376Sjeff self.conf.pack(side=LEFT) 1300139313Sjeff 1301139313Sjeff def econf(self): 1302187358Sjeff ColorConfigure(eventcolors, "Event Display Configuration") 1303139313Sjeff 1304187358Sjeff def cconf(self): 1305187358Sjeff ColorConfigure(cpucolors, "CPU Background Colors") 1306139313Sjeff 1307187359Sjeff def sconf(self): 1308187359Sjeff SourceConfigure() 1309187358Sjeff 1310139313Sjeffclass SchedGraph(Frame): 1311139313Sjeff def __init__(self, master): 1312139313Sjeff Frame.__init__(self, master) 1313139313Sjeff self.menu = None 1314139313Sjeff self.names = None 1315139313Sjeff self.display = None 1316139313Sjeff self.scale = None 1317139313Sjeff self.status = None 1318187379Sjeff self.bdheight = Y_BORDER 1319187358Sjeff self.clicksource = None 1320187358Sjeff self.lastsource = None 1321139313Sjeff self.pack(expand=1, fill="both") 1322139313Sjeff self.buildwidgets() 1323139313Sjeff self.layout() 1324278694Ssbruno self.bind_all("<Control-q>", self.quitcb) 1325139313Sjeff 1326278694Ssbruno def quitcb(self, event): 1327278694Ssbruno self.quit() 1328278694Ssbruno 1329139313Sjeff def buildwidgets(self): 1330139313Sjeff global status 1331139313Sjeff self.menu = GraphMenu(self) 1332139313Sjeff self.display = SchedDisplay(self) 1333187358Sjeff self.names = SchedNames(self, self.display) 1334139313Sjeff self.scale = Scaler(self, self.display) 1335139313Sjeff status = self.status = Status(self) 1336139313Sjeff self.scrollY = Scrollbar(self, orient="vertical", 1337139313Sjeff command=self.display_yview) 1338139313Sjeff self.display.scrollX = Scrollbar(self, orient="horizontal", 1339139313Sjeff command=self.display.xview) 1340139313Sjeff self.display["xscrollcommand"] = self.display.scrollX.set 1341139313Sjeff self.display["yscrollcommand"] = self.scrollY.set 1342139313Sjeff self.names["yscrollcommand"] = self.scrollY.set 1343139313Sjeff 1344139313Sjeff def layout(self): 1345139313Sjeff self.columnconfigure(1, weight=1) 1346139313Sjeff self.rowconfigure(1, weight=1) 1347139313Sjeff self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W) 1348139313Sjeff self.names.grid(row=1, column=0, sticky=N+S) 1349139313Sjeff self.display.grid(row=1, column=1, sticky=W+E+N+S) 1350139313Sjeff self.scrollY.grid(row=1, column=2, sticky=N+S) 1351139313Sjeff self.display.scrollX.grid(row=2, column=0, columnspan=2, 1352139313Sjeff sticky=E+W) 1353139313Sjeff self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W) 1354139313Sjeff self.status.grid(row=4, column=0, columnspan=3, sticky=E+W) 1355139313Sjeff 1356187358Sjeff def draw(self): 1357139313Sjeff self.master.update() 1358187358Sjeff self.display.prepare() 1359187358Sjeff self.names.draw() 1360139313Sjeff self.display.draw() 1361187358Sjeff self.status.startup("") 1362187471Sjeff # 1363187471Sjeff # Configure scale related values 1364187471Sjeff # 1365187471Sjeff scalemax = ktrfile.timespan() / int(self.display["width"]) 1366187471Sjeff width = int(root.geometry().split('x')[0]) 1367187471Sjeff self.constwidth = width - int(self.display["width"]) 1368187471Sjeff self.scale.setmax(scalemax) 1369187471Sjeff self.scale.set(scalemax) 1370139313Sjeff self.display.xview_moveto(0) 1371187471Sjeff self.bind("<Configure>", self.resize) 1372139313Sjeff 1373187358Sjeff def mousepress(self, event): 1374187358Sjeff self.clicksource = self.sourceat(event.y) 1375187358Sjeff 1376187376Sjeff def mousepressright(self, event): 1377187376Sjeff source = self.sourceat(event.y) 1378187376Sjeff if (source == None): 1379187376Sjeff return 1380187376Sjeff SourceContext(event, source) 1381187376Sjeff 1382187358Sjeff def mouserelease(self, event): 1383187358Sjeff if (self.clicksource == None): 1384187358Sjeff return 1385187358Sjeff newsource = self.sourceat(event.y) 1386187358Sjeff if (self.clicksource != newsource): 1387187358Sjeff self.sourceswap(self.clicksource, newsource) 1388187358Sjeff self.clicksource = None 1389187358Sjeff self.lastsource = None 1390187358Sjeff 1391187358Sjeff def mousemotion(self, event): 1392187358Sjeff if (self.clicksource == None): 1393187358Sjeff return 1394187358Sjeff newsource = self.sourceat(event.y) 1395187358Sjeff # 1396187358Sjeff # If we get a None source they moved off the page. 1397187358Sjeff # swapsource() can't handle moving multiple items so just 1398187358Sjeff # pretend we never clicked on anything to begin with so the 1399187358Sjeff # user can't mouseover a non-contiguous area. 1400187358Sjeff # 1401187358Sjeff if (newsource == None): 1402187358Sjeff self.clicksource = None 1403187358Sjeff self.lastsource = None 1404187358Sjeff return 1405187358Sjeff if (newsource == self.lastsource): 1406187358Sjeff return; 1407187358Sjeff self.lastsource = newsource 1408187358Sjeff if (newsource != self.clicksource): 1409187358Sjeff self.sourceswap(self.clicksource, newsource) 1410187358Sjeff 1411187358Sjeff # These are here because this object controls layout 1412187358Sjeff def sourcestart(self, source): 1413187358Sjeff return source.y - self.bdheight - source.ysize() 1414187358Sjeff 1415187358Sjeff def sourceend(self, source): 1416187358Sjeff return source.y + self.bdheight 1417187358Sjeff 1418187358Sjeff def sourcesize(self, source): 1419187358Sjeff return (self.bdheight * 2) + source.ysize() 1420187358Sjeff 1421187358Sjeff def sourceswap(self, source1, source2): 1422187358Sjeff # Sort so we always know which one is on top. 1423187358Sjeff if (source2.y < source1.y): 1424187358Sjeff swap = source1 1425187358Sjeff source1 = source2 1426187358Sjeff source2 = swap 1427187358Sjeff # Only swap adjacent sources 1428187358Sjeff if (self.sourceend(source1) != self.sourcestart(source2)): 1429187358Sjeff return 1430187358Sjeff # Compute start coordinates and target coordinates 1431187358Sjeff y1 = self.sourcestart(source1) 1432187358Sjeff y2 = self.sourcestart(source2) 1433187358Sjeff y1targ = y1 + self.sourcesize(source2) 1434187358Sjeff y2targ = y1 1435187358Sjeff # 1436187358Sjeff # If the sizes are not equal, adjust the start of the lower 1437187358Sjeff # source to account for the lost/gained space. 1438187358Sjeff # 1439187358Sjeff if (source1.ysize() != source2.ysize()): 1440187358Sjeff diff = source2.ysize() - source1.ysize() 1441187358Sjeff self.names.moveline(y2, diff); 1442187358Sjeff self.display.moveline(y2, diff) 1443187358Sjeff source1.move(self.display, 0, y1targ - y1) 1444187358Sjeff source2.move(self.display, 0, y2targ - y2) 1445187358Sjeff source1.movename(self.names, 0, y1targ - y1) 1446187358Sjeff source2.movename(self.names, 0, y2targ - y2) 1447187358Sjeff 1448187376Sjeff def sourcepicky(self, source): 1449187359Sjeff if (source.hidden == 0): 1450187376Sjeff return self.sourcestart(source) 1451187376Sjeff # Revert to group based sort 1452187376Sjeff sources.sort() 1453187359Sjeff prev = None 1454187359Sjeff for s in sources: 1455187359Sjeff if (s == source): 1456187359Sjeff break 1457187359Sjeff if (s.hidden == 0): 1458187359Sjeff prev = s 1459187359Sjeff if (prev == None): 1460187359Sjeff newy = 0 1461187359Sjeff else: 1462187359Sjeff newy = self.sourcestart(prev) + self.sourcesize(prev) 1463187376Sjeff return newy 1464187376Sjeff 1465187376Sjeff def sourceshow(self, source): 1466187376Sjeff if (source.hidden == 0): 1467187376Sjeff return; 1468187376Sjeff newy = self.sourcepicky(source) 1469187376Sjeff off = newy - self.sourcestart(source) 1470187359Sjeff self.sourceshiftall(newy-1, self.sourcesize(source)) 1471187359Sjeff self.sourceshift(source, off) 1472187359Sjeff source.hidden = 0 1473187359Sjeff 1474187376Sjeff # 1475187376Sjeff # Optimized source show of multiple entries that only moves each 1476187376Sjeff # existing entry once. Doing sourceshow() iteratively is too 1477187376Sjeff # expensive due to python's canvas.move(). 1478187376Sjeff # 1479187376Sjeff def sourceshowlist(self, srclist): 1480187376Sjeff srclist.sort(cmp=source_cmp_start) 1481187376Sjeff startsize = [] 1482187376Sjeff for source in srclist: 1483187376Sjeff if (source.hidden == 0): 1484187376Sjeff srclist.remove(source) 1485187376Sjeff startsize.append((self.sourcepicky(source), 1486187376Sjeff self.sourcesize(source))) 1487187376Sjeff 1488187376Sjeff sources.sort(cmp=source_cmp_start, reverse=True) 1489187376Sjeff self.status.startup("Updating display..."); 1490187376Sjeff for source in sources: 1491187376Sjeff if (source.hidden == 1): 1492187376Sjeff continue 1493187376Sjeff nstart = self.sourcestart(source) 1494187376Sjeff size = 0 1495187376Sjeff for hidden in startsize: 1496187376Sjeff (start, sz) = hidden 1497187376Sjeff if (start <= nstart or start+sz <= nstart): 1498187376Sjeff size += sz 1499187376Sjeff self.sourceshift(source, size) 1500187376Sjeff idx = 0 1501187376Sjeff size = 0 1502187376Sjeff for source in srclist: 1503187376Sjeff (newy, sz) = startsize[idx] 1504187376Sjeff off = (newy + size) - self.sourcestart(source) 1505187376Sjeff self.sourceshift(source, off) 1506187376Sjeff source.hidden = 0 1507187376Sjeff size += sz 1508187376Sjeff idx += 1 1509187471Sjeff self.updatescroll() 1510187376Sjeff self.status.set("") 1511187376Sjeff 1512187376Sjeff # 1513187376Sjeff # Optimized source hide of multiple entries that only moves each 1514187376Sjeff # remaining entry once. Doing sourcehide() iteratively is too 1515187376Sjeff # expensive due to python's canvas.move(). 1516187376Sjeff # 1517187376Sjeff def sourcehidelist(self, srclist): 1518187376Sjeff srclist.sort(cmp=source_cmp_start) 1519187376Sjeff sources.sort(cmp=source_cmp_start) 1520187376Sjeff startsize = [] 1521187376Sjeff off = len(sources) * 100 1522187376Sjeff self.status.startup("Updating display..."); 1523187376Sjeff for source in srclist: 1524187376Sjeff if (source.hidden == 1): 1525187376Sjeff srclist.remove(source) 1526187376Sjeff # 1527187376Sjeff # Remember our old position so we can sort things 1528187376Sjeff # below us when we're done. 1529187376Sjeff # 1530187376Sjeff startsize.append((self.sourcestart(source), 1531187376Sjeff self.sourcesize(source))) 1532187376Sjeff self.sourceshift(source, off) 1533187376Sjeff source.hidden = 1 1534187376Sjeff 1535187376Sjeff idx = 0 1536187376Sjeff size = 0 1537187376Sjeff for hidden in startsize: 1538187376Sjeff (start, sz) = hidden 1539187376Sjeff size += sz 1540187376Sjeff if (idx + 1 < len(startsize)): 1541187376Sjeff (stop, sz) = startsize[idx+1] 1542187376Sjeff else: 1543187376Sjeff stop = self.display.ysize() 1544187376Sjeff idx += 1 1545187376Sjeff for source in sources: 1546187376Sjeff nstart = self.sourcestart(source) 1547187376Sjeff if (nstart < start or source.hidden == 1): 1548187376Sjeff continue 1549187376Sjeff if (nstart >= stop): 1550187376Sjeff break; 1551187376Sjeff self.sourceshift(source, -size) 1552187471Sjeff self.updatescroll() 1553187376Sjeff self.status.set("") 1554187376Sjeff 1555187359Sjeff def sourcehide(self, source): 1556187376Sjeff if (source.hidden == 1): 1557187376Sjeff return; 1558187359Sjeff # Move it out of the visible area 1559187359Sjeff off = len(sources) * 100 1560187359Sjeff start = self.sourcestart(source) 1561187359Sjeff self.sourceshift(source, off) 1562187359Sjeff self.sourceshiftall(start, -self.sourcesize(source)) 1563187359Sjeff source.hidden = 1 1564187359Sjeff 1565187359Sjeff def sourceshift(self, source, off): 1566187359Sjeff start = self.sourcestart(source) 1567187359Sjeff source.move(self.display, 0, off) 1568187359Sjeff source.movename(self.names, 0, off) 1569187359Sjeff self.names.moveline(start, off); 1570187359Sjeff self.display.moveline(start, off) 1571187376Sjeff # 1572187376Sjeff # We update the idle tasks to shrink the dirtied area so 1573187376Sjeff # it does not always include the entire screen. 1574187376Sjeff # 1575187376Sjeff self.names.update_idletasks() 1576187376Sjeff self.display.update_idletasks() 1577187359Sjeff 1578187359Sjeff def sourceshiftall(self, start, off): 1579187376Sjeff self.status.startup("Updating display..."); 1580187359Sjeff for source in sources: 1581187359Sjeff nstart = self.sourcestart(source) 1582187359Sjeff if (nstart < start): 1583187359Sjeff continue; 1584187359Sjeff self.sourceshift(source, off) 1585187471Sjeff self.updatescroll() 1586187376Sjeff self.status.set("") 1587187359Sjeff 1588187358Sjeff def sourceat(self, ypos): 1589187358Sjeff (start, end) = self.names.yview() 1590187358Sjeff starty = start * float(self.names.ysize) 1591187358Sjeff ypos += starty 1592187358Sjeff for source in sources: 1593187359Sjeff if (source.hidden == 1): 1594187359Sjeff continue; 1595187358Sjeff yend = self.sourceend(source) 1596187358Sjeff ystart = self.sourcestart(source) 1597187358Sjeff if (ypos >= ystart and ypos <= yend): 1598187358Sjeff return source 1599187358Sjeff return None 1600187358Sjeff 1601139313Sjeff def display_yview(self, *args): 1602139313Sjeff self.names.yview(*args) 1603139313Sjeff self.display.yview(*args) 1604139313Sjeff 1605187471Sjeff def resize(self, *args): 1606187471Sjeff width = int(root.geometry().split('x')[0]) 1607187471Sjeff scalemax = ktrfile.timespan() / (width - self.constwidth) 1608187471Sjeff self.scale.setmax(scalemax) 1609187471Sjeff 1610187471Sjeff def updatescroll(self): 1611187471Sjeff self.names.updatescroll() 1612187471Sjeff self.display.updatescroll() 1613187471Sjeff 1614139313Sjeff def setcolor(self, tag, color): 1615139313Sjeff self.display.setcolor(tag, color) 1616139313Sjeff 1617139313Sjeff def hide(self, tag): 1618139313Sjeff self.display.hide(tag) 1619139313Sjeff 1620187358Sjeff def getcolor(self, tag): 1621187358Sjeff return self.display.getcolor(tag) 1622187358Sjeff 1623187358Sjeff def getstate(self, tag): 1624187358Sjeff return self.display.getstate(tag) 1625187358Sjeff 1626187358Sjeffif (len(sys.argv) != 2 and len(sys.argv) != 3): 1627187358Sjeff print "usage:", sys.argv[0], "<ktr file> [clock freq in ghz]" 1628139313Sjeff sys.exit(1) 1629139313Sjeff 1630187358Sjeffif (len(sys.argv) > 2): 1631187358Sjeff clockfreq = float(sys.argv[2]) 1632187358Sjeff 1633139313Sjeffroot = Tk() 1634187379Sjeffroot.title("SchedGraph") 1635187358Sjeffcolormap = Colormap(eventcolors) 1636187358Sjeffcpucolormap = Colormap(cpucolors) 1637139313Sjeffgraph = SchedGraph(root) 1638187358Sjeffktrfile = KTRFile(sys.argv[1]) 1639187358Sjeffgraph.draw() 1640139313Sjeffroot.mainloop() 1641