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"),
73187358Sjeff	("yielding",	"yellow"),
74187358Sjeff	("swapped",	"violet"),
75187358Sjeff	("suspended",	"purple"),
76187358Sjeff	("iwait",	"grey"),
77187358Sjeff	("sleep",	"blue"),
78187358Sjeff	("blocked",	"dark red"),
79187358Sjeff	("runq add",	"yellow"),
80187358Sjeff	("runq rem",	"yellow"),
81187358Sjeff	("thread exit",	"grey"),
82187358Sjeff	("proc exit",	"grey"),
83187358Sjeff	("callwheel idle", "grey"),
84187358Sjeff	("callout running", "green"),
85187358Sjeff	("lock acquire", "blue"),
86187358Sjeff	("lock contest", "purple"),
87187358Sjeff	("failed lock try", "red"),
88187358Sjeff	("lock release", "grey"),
89187379Sjeff	("statclock",	"black"),
90187358Sjeff	("prio",	"black"),
91187358Sjeff	("lend prio",	"black"),
92187358Sjeff	("wokeup",	"black")
93187358Sjeff]
94187358Sjeff
95187358Sjeffcpucolors = [
96187358Sjeff	("CPU 0",	"light grey"),
97187358Sjeff	("CPU 1",	"dark grey"),
98187358Sjeff	("CPU 2",	"light blue"),
99187358Sjeff	("CPU 3",	"light pink"),
100187358Sjeff	("CPU 4",	"blanched almond"),
101187358Sjeff	("CPU 5",	"slate grey"),
102187358Sjeff	("CPU 6",	"tan"),
103187358Sjeff	("CPU 7",	"thistle"),
104187358Sjeff	("CPU 8",	"white")
105187358Sjeff]
106187358Sjeff
107187358Sjeffcolors = [
108187358Sjeff	"white", "thistle", "blanched almond", "tan", "chartreuse",
109187358Sjeff	"dark red", "red", "pale violet red", "pink", "light pink",
110187358Sjeff	"dark orange", "orange", "coral", "light coral",
111187358Sjeff	"goldenrod", "gold", "yellow", "light yellow",
112187358Sjeff	"dark green", "green", "light green", "light sea green",
113187358Sjeff	"dark blue", "blue", "light blue", "steel blue", "light slate blue",
114187358Sjeff	"dark violet", "violet", "purple", "blue violet",
115187358Sjeff	"dark grey", "slate grey", "light grey",
116187358Sjeff	"black",
117187358Sjeff]
118187358Sjeffcolors.sort()
119187358Sjeff
120139313Sjeffticksps = None
121139313Sjeffstatus = None
122187358Sjeffcolormap = None
123187358Sjeffktrfile = None
124187358Sjeffclockfreq = None
125187358Sjeffsources = []
126173743Ssamlineno = -1
127139313Sjeff
128187379SjeffY_BORDER = 10
129187379SjeffX_BORDER = 10
130187379SjeffY_COUNTER = 80
131187379SjeffY_EVENTSOURCE = 10
132187379SjeffXY_POINT = 4
133187379Sjeff
134187358Sjeffclass Colormap:
135187358Sjeff	def __init__(self, table):
136187358Sjeff		self.table = table
137187358Sjeff		self.map = {}
138187358Sjeff		for entry in table:
139187358Sjeff			self.map[entry[0]] = entry[1]
140187358Sjeff
141187358Sjeff	def lookup(self, name):
142187358Sjeff		try:
143187358Sjeff			color = self.map[name]
144187358Sjeff		except:
145187358Sjeff			color = colors[random.randrange(0, len(colors))]
146187358Sjeff			print "Picking random color", color, "for", name
147187358Sjeff			self.map[name] = color
148187358Sjeff			self.table.append((name, color))
149187358Sjeff		return (color)
150187358Sjeff
151139313Sjeffdef ticks2sec(ticks):
152187580Sjeff	ticks = float(ticks)
153187580Sjeff	ns = float(ticksps) / 1000000000
154187580Sjeff	ticks /= ns
155139313Sjeff	if (ticks < 1000):
156187580Sjeff		return ("%.2fns" % ticks)
157139313Sjeff	ticks /= 1000
158139313Sjeff	if (ticks < 1000):
159187580Sjeff		return ("%.2fus" % ticks)
160139313Sjeff	ticks /= 1000
161187580Sjeff	if (ticks < 1000):
162187580Sjeff		return ("%.2fms" % ticks)
163187580Sjeff	ticks /= 1000
164187580Sjeff	return ("%.2fs" % ticks)
165139313Sjeff
166139313Sjeffclass Scaler(Frame):
167139313Sjeff	def __init__(self, master, target):
168139313Sjeff		Frame.__init__(self, master)
169187471Sjeff		self.scale = None
170187471Sjeff		self.target = target
171139313Sjeff		self.label = Label(self, text="Ticks per pixel")
172139313Sjeff		self.label.pack(side=LEFT)
173187471Sjeff		self.resolution = 100
174187471Sjeff		self.setmax(10000)
175139313Sjeff
176139313Sjeff	def scaleset(self, value):
177139313Sjeff		self.target.scaleset(int(value))
178139313Sjeff
179139313Sjeff	def set(self, value):
180139313Sjeff		self.scale.set(value)
181139313Sjeff
182187471Sjeff	def setmax(self, value):
183187471Sjeff		#
184187471Sjeff		# We can't reconfigure the to_ value so we delete the old
185187471Sjeff		# window and make a new one when we resize.
186187471Sjeff		#
187187471Sjeff		if (self.scale != None):
188187471Sjeff			self.scale.pack_forget()
189187471Sjeff			self.scale.destroy()
190187471Sjeff		self.scale = Scale(self, command=self.scaleset,
191187471Sjeff		    from_=100, to_=value, orient=HORIZONTAL,
192187471Sjeff		    resolution=self.resolution)
193187471Sjeff		self.scale.pack(fill="both", expand=1)
194187471Sjeff		self.scale.set(self.target.scaleget())
195187471Sjeff
196139313Sjeffclass Status(Frame):
197139313Sjeff	def __init__(self, master):
198139313Sjeff		Frame.__init__(self, master)
199139313Sjeff		self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
200139313Sjeff		self.label.pack(fill="both", expand=1)
201139313Sjeff		self.clear()
202139313Sjeff
203139313Sjeff	def set(self, str):
204139313Sjeff		self.label.config(text=str)
205139313Sjeff
206139313Sjeff	def clear(self):
207139313Sjeff		self.label.config(text="")
208139313Sjeff
209139313Sjeff	def startup(self, str):
210139313Sjeff		self.set(str)
211139313Sjeff		root.update()
212139313Sjeff
213187358Sjeffclass ColorConf(Frame):
214187358Sjeff	def __init__(self, master, name, color):
215139313Sjeff		Frame.__init__(self, master)
216187358Sjeff		if (graph.getstate(name) == "hidden"):
217187358Sjeff			enabled = 0
218187358Sjeff		else:
219187358Sjeff			enabled = 1
220139313Sjeff		self.name = name
221139313Sjeff		self.color = StringVar()
222139313Sjeff		self.color_default = color
223139313Sjeff		self.color_current = color
224139313Sjeff		self.color.set(color)
225139313Sjeff		self.enabled = IntVar()
226139313Sjeff		self.enabled_default = enabled
227139313Sjeff		self.enabled_current = enabled
228139313Sjeff		self.enabled.set(enabled)
229139313Sjeff		self.draw()
230139313Sjeff
231139313Sjeff	def draw(self):
232139313Sjeff		self.label = Label(self, text=self.name, anchor=W)
233139313Sjeff		self.sample = Canvas(self, width=24, height=24,
234139313Sjeff		    bg='grey')
235139313Sjeff		self.rect = self.sample.create_rectangle(0, 0, 24, 24,
236139313Sjeff		    fill=self.color.get())
237187358Sjeff		self.list = OptionMenu(self, self.color, command=self.setcolor,
238187358Sjeff		    *colors)
239139313Sjeff		self.checkbox = Checkbutton(self, text="enabled",
240139313Sjeff		    variable=self.enabled)
241139313Sjeff		self.label.grid(row=0, column=0, sticky=E+W)
242139313Sjeff		self.sample.grid(row=0, column=1)
243139313Sjeff		self.list.grid(row=0, column=2, sticky=E+W)
244139313Sjeff		self.checkbox.grid(row=0, column=3)
245139313Sjeff		self.columnconfigure(0, weight=1)
246187358Sjeff		self.columnconfigure(2, minsize=150)
247139313Sjeff
248139313Sjeff	def setcolor(self, color):
249139313Sjeff		self.color.set(color)
250139313Sjeff		self.sample.itemconfigure(self.rect, fill=color)
251139313Sjeff
252139313Sjeff	def apply(self):
253139313Sjeff		cchange = 0
254139313Sjeff		echange = 0
255139313Sjeff		if (self.color_current != self.color.get()):
256139313Sjeff			cchange = 1
257139313Sjeff		if (self.enabled_current != self.enabled.get()):
258139313Sjeff			echange = 1
259139313Sjeff		self.color_current = self.color.get()
260139313Sjeff		self.enabled_current = self.enabled.get()
261139313Sjeff		if (echange != 0):
262139313Sjeff			if (self.enabled_current):
263139313Sjeff				graph.setcolor(self.name, self.color_current)
264139313Sjeff			else:
265139313Sjeff				graph.hide(self.name)
266139313Sjeff			return
267139313Sjeff		if (cchange != 0):
268139313Sjeff			graph.setcolor(self.name, self.color_current)
269139313Sjeff
270139313Sjeff	def revert(self):
271139313Sjeff		self.setcolor(self.color_default)
272139313Sjeff		self.enabled.set(self.enabled_default)
273139313Sjeff
274187358Sjeffclass ColorConfigure(Toplevel):
275187358Sjeff	def __init__(self, table, name):
276139313Sjeff		Toplevel.__init__(self)
277139313Sjeff		self.resizable(0, 0)
278187358Sjeff		self.title(name)
279187358Sjeff		self.items = LabelFrame(self, text="Item Type")
280139313Sjeff		self.buttons = Frame(self)
281139313Sjeff		self.drawbuttons()
282139313Sjeff		self.items.grid(row=0, column=0, sticky=E+W)
283139313Sjeff		self.columnconfigure(0, weight=1)
284139313Sjeff		self.buttons.grid(row=1, column=0, sticky=E+W)
285139313Sjeff		self.types = []
286139313Sjeff		self.irow = 0
287187358Sjeff		for type in table:
288187358Sjeff			color = graph.getcolor(type[0])
289187358Sjeff			if (color != ""):
290187358Sjeff				self.additem(type[0], color)
291139313Sjeff
292187358Sjeff	def additem(self, name, color):
293187358Sjeff		item = ColorConf(self.items, name, color)
294139313Sjeff		self.types.append(item)
295139313Sjeff		item.grid(row=self.irow, column=0, sticky=E+W)
296139313Sjeff		self.irow += 1
297139313Sjeff
298139313Sjeff	def drawbuttons(self):
299139313Sjeff		self.apply = Button(self.buttons, text="Apply",
300139313Sjeff		    command=self.apress)
301187358Sjeff		self.default = Button(self.buttons, text="Revert",
302139313Sjeff		    command=self.rpress)
303139313Sjeff		self.apply.grid(row=0, column=0, sticky=E+W)
304187358Sjeff		self.default.grid(row=0, column=1, sticky=E+W)
305139313Sjeff		self.buttons.columnconfigure(0, weight=1)
306139313Sjeff		self.buttons.columnconfigure(1, weight=1)
307139313Sjeff
308139313Sjeff	def apress(self):
309139313Sjeff		for item in self.types:
310139313Sjeff			item.apply()
311139313Sjeff
312139313Sjeff	def rpress(self):
313139313Sjeff		for item in self.types:
314139313Sjeff			item.revert()
315139313Sjeff
316187359Sjeffclass SourceConf(Frame):
317187359Sjeff	def __init__(self, master, source):
318187359Sjeff		Frame.__init__(self, master)
319187359Sjeff		if (source.hidden == 1):
320187359Sjeff			enabled = 0
321187359Sjeff		else:
322187359Sjeff			enabled = 1
323187359Sjeff		self.source = source
324187359Sjeff		self.name = source.name
325187359Sjeff		self.enabled = IntVar()
326187359Sjeff		self.enabled_default = enabled
327187359Sjeff		self.enabled_current = enabled
328187359Sjeff		self.enabled.set(enabled)
329187359Sjeff		self.draw()
330187359Sjeff
331187359Sjeff	def draw(self):
332187359Sjeff		self.label = Label(self, text=self.name, anchor=W)
333187359Sjeff		self.checkbox = Checkbutton(self, text="enabled",
334187359Sjeff		    variable=self.enabled)
335187359Sjeff		self.label.grid(row=0, column=0, sticky=E+W)
336187359Sjeff		self.checkbox.grid(row=0, column=1)
337187359Sjeff		self.columnconfigure(0, weight=1)
338187359Sjeff
339187376Sjeff	def changed(self):
340187376Sjeff		if (self.enabled_current != self.enabled.get()):
341187376Sjeff			return 1
342187376Sjeff		return 0
343187376Sjeff
344187359Sjeff	def apply(self):
345187359Sjeff		self.enabled_current = self.enabled.get()
346187359Sjeff
347187359Sjeff	def revert(self):
348187359Sjeff		self.enabled.set(self.enabled_default)
349187359Sjeff
350187359Sjeff	def check(self):
351187359Sjeff		self.enabled.set(1)
352187359Sjeff
353187359Sjeff	def uncheck(self):
354187359Sjeff		self.enabled.set(0)
355187359Sjeff
356187359Sjeffclass SourceConfigure(Toplevel):
357187359Sjeff	def __init__(self):
358187359Sjeff		Toplevel.__init__(self)
359187359Sjeff		self.resizable(0, 0)
360187359Sjeff		self.title("Source Configuration")
361187359Sjeff		self.items = []
362187359Sjeff		self.iframe = Frame(self)
363187359Sjeff		self.iframe.grid(row=0, column=0, sticky=E+W)
364187359Sjeff		f = LabelFrame(self.iframe, bd=4, text="Sources")
365187359Sjeff		self.items.append(f)
366187359Sjeff		self.buttons = Frame(self)
367187359Sjeff		self.items[0].grid(row=0, column=0, sticky=E+W)
368187359Sjeff		self.columnconfigure(0, weight=1)
369187359Sjeff		self.sconfig = []
370187359Sjeff		self.irow = 0
371187359Sjeff		self.icol = 0
372187359Sjeff		for source in sources:
373187359Sjeff			self.addsource(source)
374187359Sjeff		self.drawbuttons()
375187359Sjeff		self.buttons.grid(row=1, column=0, sticky=W)
376187359Sjeff
377187359Sjeff	def addsource(self, source):
378187359Sjeff		if (self.irow > 30):
379187359Sjeff			self.icol += 1
380187359Sjeff			self.irow = 0
381187359Sjeff			c = self.icol
382187359Sjeff			f = LabelFrame(self.iframe, bd=4, text="Sources")
383187359Sjeff			f.grid(row=0, column=c, sticky=N+E+W)
384187359Sjeff			self.items.append(f)
385187359Sjeff		item = SourceConf(self.items[self.icol], source)
386187359Sjeff		self.sconfig.append(item)
387187359Sjeff		item.grid(row=self.irow, column=0, sticky=E+W)
388187359Sjeff		self.irow += 1
389187359Sjeff
390187359Sjeff	def drawbuttons(self):
391187359Sjeff		self.apply = Button(self.buttons, text="Apply",
392187359Sjeff		    command=self.apress)
393187359Sjeff		self.default = Button(self.buttons, text="Revert",
394187359Sjeff		    command=self.rpress)
395187359Sjeff		self.checkall = Button(self.buttons, text="Check All",
396187359Sjeff		    command=self.cpress)
397187359Sjeff		self.uncheckall = Button(self.buttons, text="Uncheck All",
398187359Sjeff		    command=self.upress)
399187359Sjeff		self.checkall.grid(row=0, column=0, sticky=W)
400187359Sjeff		self.uncheckall.grid(row=0, column=1, sticky=W)
401187359Sjeff		self.apply.grid(row=0, column=2, sticky=W)
402187359Sjeff		self.default.grid(row=0, column=3, sticky=W)
403187359Sjeff		self.buttons.columnconfigure(0, weight=1)
404187359Sjeff		self.buttons.columnconfigure(1, weight=1)
405187359Sjeff		self.buttons.columnconfigure(2, weight=1)
406187359Sjeff		self.buttons.columnconfigure(3, weight=1)
407187359Sjeff
408187359Sjeff	def apress(self):
409187376Sjeff		disable_sources = []
410187376Sjeff		enable_sources = []
411187359Sjeff		for item in self.sconfig:
412187376Sjeff			if (item.changed() == 0):
413187376Sjeff				continue
414187376Sjeff			if (item.enabled.get() == 1):
415187376Sjeff				enable_sources.append(item.source)
416187376Sjeff			else:
417187376Sjeff				disable_sources.append(item.source)
418187376Sjeff
419187376Sjeff		if (len(disable_sources)):
420187376Sjeff			graph.sourcehidelist(disable_sources)
421187376Sjeff		if (len(enable_sources)):
422187376Sjeff			graph.sourceshowlist(enable_sources)
423187376Sjeff
424187376Sjeff		for item in self.sconfig:
425187359Sjeff			item.apply()
426187359Sjeff
427187359Sjeff	def rpress(self):
428187359Sjeff		for item in self.sconfig:
429187359Sjeff			item.revert()
430187359Sjeff
431187359Sjeff	def cpress(self):
432187359Sjeff		for item in self.sconfig:
433187359Sjeff			item.check()
434187359Sjeff
435187359Sjeff	def upress(self):
436187359Sjeff		for item in self.sconfig:
437187359Sjeff			item.uncheck()
438187359Sjeff
439187376Sjeff# Reverse compare of second member of the tuple
440187376Sjeffdef cmp_counts(x, y):
441187376Sjeff	return y[1] - x[1]
442187376Sjeff
443187376Sjeffclass SourceStats(Toplevel):
444187376Sjeff	def __init__(self, source):
445187376Sjeff		self.source = source
446187376Sjeff		Toplevel.__init__(self)
447187376Sjeff		self.resizable(0, 0)
448187376Sjeff		self.title(source.name + " statistics")
449187376Sjeff		self.evframe = LabelFrame(self,
450187580Sjeff		    text="Event Count, Duration, Avg Duration")
451187376Sjeff		self.evframe.grid(row=0, column=0, sticky=E+W)
452187376Sjeff		eventtypes={}
453187376Sjeff		for event in self.source.events:
454187376Sjeff			if (event.type == "pad"):
455187376Sjeff				continue
456187376Sjeff			duration = event.duration
457187376Sjeff			if (eventtypes.has_key(event.name)):
458187376Sjeff				(c, d) = eventtypes[event.name]
459187376Sjeff				c += 1
460187376Sjeff				d += duration
461187376Sjeff				eventtypes[event.name] = (c, d)
462187376Sjeff			else:
463187376Sjeff				eventtypes[event.name] = (1, duration)
464187376Sjeff		events = []
465187376Sjeff		for k, v in eventtypes.iteritems():
466187376Sjeff			(c, d) = v
467187376Sjeff			events.append((k, c, d))
468187376Sjeff		events.sort(cmp=cmp_counts)
469187376Sjeff
470187376Sjeff		ypos = 0
471187376Sjeff		for event in events:
472187376Sjeff			(name, c, d) = event
473187580Sjeff			Label(self.evframe, text=name, bd=1,
474187580Sjeff			    relief=SUNKEN, anchor=W, width=30).grid(
475187580Sjeff			    row=ypos, column=0, sticky=W+E)
476187580Sjeff			Label(self.evframe, text=str(c), bd=1,
477187580Sjeff			    relief=SUNKEN, anchor=W, width=10).grid(
478187580Sjeff			    row=ypos, column=1, sticky=W+E)
479187580Sjeff			Label(self.evframe, text=ticks2sec(d),
480187580Sjeff			    bd=1, relief=SUNKEN, width=10).grid(
481187580Sjeff			    row=ypos, column=2, sticky=W+E)
482187580Sjeff			if (d and c):
483187580Sjeff				d /= c
484187580Sjeff			else:
485187580Sjeff				d = 0
486187580Sjeff			Label(self.evframe, text=ticks2sec(d),
487187580Sjeff			    bd=1, relief=SUNKEN, width=10).grid(
488187580Sjeff			    row=ypos, column=3, sticky=W+E)
489187376Sjeff			ypos += 1
490187376Sjeff
491187376Sjeff
492187376Sjeffclass SourceContext(Menu):
493187376Sjeff	def __init__(self, event, source):
494187376Sjeff		self.source = source
495187376Sjeff		Menu.__init__(self, tearoff=0, takefocus=0)
496187376Sjeff		self.add_command(label="hide", command=self.hide)
497187376Sjeff		self.add_command(label="hide group", command=self.hidegroup)
498187376Sjeff		self.add_command(label="stats", command=self.stats)
499187376Sjeff		self.tk_popup(event.x_root-3, event.y_root+3)
500187376Sjeff
501187376Sjeff	def hide(self):
502187376Sjeff		graph.sourcehide(self.source)
503187376Sjeff
504187376Sjeff	def hidegroup(self):
505187376Sjeff		grouplist = []
506187376Sjeff		for source in sources:
507187376Sjeff			if (source.group == self.source.group):
508187376Sjeff				grouplist.append(source)
509187376Sjeff		graph.sourcehidelist(grouplist)
510187376Sjeff
511187376Sjeff	def show(self):
512187376Sjeff		graph.sourceshow(self.source)
513187376Sjeff
514187376Sjeff	def stats(self):
515187376Sjeff		SourceStats(self.source)
516187376Sjeff
517139313Sjeffclass EventView(Toplevel):
518139313Sjeff	def __init__(self, event, canvas):
519139313Sjeff		Toplevel.__init__(self)
520139313Sjeff		self.resizable(0, 0)
521139313Sjeff		self.title("Event")
522139313Sjeff		self.event = event
523187358Sjeff		self.buttons = Frame(self)
524187358Sjeff		self.buttons.grid(row=0, column=0, sticky=E+W)
525139313Sjeff		self.frame = Frame(self)
526187358Sjeff		self.frame.grid(row=1, column=0, sticky=N+S+E+W)
527139313Sjeff		self.canvas = canvas
528139313Sjeff		self.drawlabels()
529139313Sjeff		self.drawbuttons()
530139313Sjeff		event.displayref(canvas)
531139313Sjeff		self.bind("<Destroy>", self.destroycb)
532139313Sjeff
533139313Sjeff	def destroycb(self, event):
534139313Sjeff		self.unbind("<Destroy>")
535139313Sjeff		if (self.event != None):
536139313Sjeff			self.event.displayunref(self.canvas)
537139313Sjeff			self.event = None
538139313Sjeff		self.destroy()
539139313Sjeff
540139313Sjeff	def clearlabels(self):
541139313Sjeff		for label in self.frame.grid_slaves():
542139313Sjeff			label.grid_remove()
543139313Sjeff
544139313Sjeff	def drawlabels(self):
545139313Sjeff		ypos = 0
546139313Sjeff		labels = self.event.labels()
547139313Sjeff		while (len(labels) < 7):
548187358Sjeff			labels.append(("", ""))
549139313Sjeff		for label in labels:
550187358Sjeff			name, value = label
551187358Sjeff			linked = 0
552187358Sjeff			if (name == "linkedto"):
553187358Sjeff				linked = 1
554139313Sjeff			l = Label(self.frame, text=name, bd=1, width=15,
555139313Sjeff			    relief=SUNKEN, anchor=W)
556139313Sjeff			if (linked):
557139313Sjeff				fgcolor = "blue"
558139313Sjeff			else:
559139313Sjeff				fgcolor = "black"
560139313Sjeff			r = Label(self.frame, text=value, bd=1,
561139313Sjeff			    relief=SUNKEN, anchor=W, fg=fgcolor)
562139313Sjeff			l.grid(row=ypos, column=0, sticky=E+W)
563139313Sjeff			r.grid(row=ypos, column=1, sticky=E+W)
564139313Sjeff			if (linked):
565139313Sjeff				r.bind("<Button-1>", self.linkpress)
566139313Sjeff			ypos += 1
567139313Sjeff		self.frame.columnconfigure(1, minsize=80)
568139313Sjeff
569139313Sjeff	def drawbuttons(self):
570139313Sjeff		self.back = Button(self.buttons, text="<", command=self.bpress)
571139313Sjeff		self.forw = Button(self.buttons, text=">", command=self.fpress)
572139313Sjeff		self.new = Button(self.buttons, text="new", command=self.npress)
573139313Sjeff		self.back.grid(row=0, column=0, sticky=E+W)
574139313Sjeff		self.forw.grid(row=0, column=1, sticky=E+W)
575139313Sjeff		self.new.grid(row=0, column=2, sticky=E+W)
576139313Sjeff		self.buttons.columnconfigure(2, weight=1)
577139313Sjeff
578139313Sjeff	def newevent(self, event):
579139313Sjeff		self.event.displayunref(self.canvas)
580139313Sjeff		self.clearlabels()
581139313Sjeff		self.event = event
582139313Sjeff		self.event.displayref(self.canvas)
583139313Sjeff		self.drawlabels()
584139313Sjeff
585139313Sjeff	def npress(self):
586139313Sjeff		EventView(self.event, self.canvas)
587139313Sjeff
588139313Sjeff	def bpress(self):
589139313Sjeff		prev = self.event.prev()
590139313Sjeff		if (prev == None):
591139313Sjeff			return
592187358Sjeff		while (prev.type == "pad"):
593139313Sjeff			prev = prev.prev()
594139313Sjeff			if (prev == None):
595139313Sjeff				return
596139313Sjeff		self.newevent(prev)
597139313Sjeff
598139313Sjeff	def fpress(self):
599139313Sjeff		next = self.event.next()
600139313Sjeff		if (next == None):
601139313Sjeff			return
602187358Sjeff		while (next.type == "pad"):
603139313Sjeff			next = next.next()
604139313Sjeff			if (next == None):
605139313Sjeff				return
606139313Sjeff		self.newevent(next)
607139313Sjeff
608139313Sjeff	def linkpress(self, wevent):
609139313Sjeff		event = self.event.getlinked()
610139313Sjeff		if (event != None):
611139313Sjeff			self.newevent(event)
612139313Sjeff
613139313Sjeffclass Event:
614187358Sjeff	def __init__(self, source, name, cpu, timestamp, attrs):
615139313Sjeff		self.source = source
616187358Sjeff		self.name = name
617139313Sjeff		self.cpu = cpu
618139313Sjeff		self.timestamp = int(timestamp)
619187358Sjeff		self.attrs = attrs
620139313Sjeff		self.idx = None
621139313Sjeff		self.item = None
622139313Sjeff		self.dispcnt = 0
623187376Sjeff		self.duration = 0
624173743Ssam		self.recno = lineno
625139313Sjeff
626139313Sjeff	def status(self):
627139313Sjeff		statstr = self.name + " " + self.source.name
628139313Sjeff		statstr += " on: cpu" + str(self.cpu)
629139313Sjeff		statstr += " at: " + str(self.timestamp)
630187358Sjeff		statstr += " attributes: "
631187358Sjeff		for i in range(0, len(self.attrs)):
632187358Sjeff			attr = self.attrs[i]
633187358Sjeff			statstr += attr[0] + ": " + str(attr[1])
634187358Sjeff			if (i != len(self.attrs) - 1):
635187358Sjeff				statstr += ", "
636139313Sjeff		status.set(statstr)
637139313Sjeff
638187358Sjeff	def labels(self):
639187358Sjeff		return [("Source", self.source.name),
640187358Sjeff			("Event", self.name),
641187358Sjeff			("CPU", self.cpu),
642187358Sjeff			("Timestamp", self.timestamp),
643187358Sjeff			("KTR Line ", self.recno)
644187358Sjeff		] + self.attrs
645139313Sjeff
646187358Sjeff	def mouseenter(self, canvas):
647139313Sjeff		self.displayref(canvas)
648139313Sjeff		self.status()
649139313Sjeff
650187358Sjeff	def mouseexit(self, canvas):
651139313Sjeff		self.displayunref(canvas)
652139313Sjeff		status.clear()
653139313Sjeff
654187358Sjeff	def mousepress(self, canvas):
655139313Sjeff		EventView(self, canvas)
656139313Sjeff
657187358Sjeff	def draw(self, canvas, xpos, ypos, item):
658187358Sjeff		self.item = item
659187358Sjeff		if (item != None):
660187358Sjeff			canvas.items[item] = self
661187358Sjeff
662187358Sjeff	def move(self, canvas, x, y):
663187358Sjeff		if (self.item == None):
664187358Sjeff			return;
665187358Sjeff		canvas.move(self.item, x, y);
666187358Sjeff
667139313Sjeff	def next(self):
668139313Sjeff		return self.source.eventat(self.idx + 1)
669139313Sjeff
670187358Sjeff	def nexttype(self, type):
671187358Sjeff		next = self.next()
672187358Sjeff		while (next != None and next.type != type):
673187358Sjeff			next = next.next()
674187358Sjeff		return (next)
675187358Sjeff
676139313Sjeff	def prev(self):
677139313Sjeff		return self.source.eventat(self.idx - 1)
678139313Sjeff
679139313Sjeff	def displayref(self, canvas):
680139313Sjeff		if (self.dispcnt == 0):
681139313Sjeff			canvas.itemconfigure(self.item, width=2)
682139313Sjeff		self.dispcnt += 1
683139313Sjeff
684139313Sjeff	def displayunref(self, canvas):
685139313Sjeff		self.dispcnt -= 1
686139313Sjeff		if (self.dispcnt == 0):
687139313Sjeff			canvas.itemconfigure(self.item, width=0)
688139313Sjeff			canvas.tag_raise("point", "state")
689139313Sjeff
690139313Sjeff	def getlinked(self):
691187358Sjeff		for attr in self.attrs:
692187358Sjeff			if (attr[0] != "linkedto"):
693187358Sjeff				continue
694187358Sjeff			source = ktrfile.findid(attr[1])
695187358Sjeff			return source.findevent(self.timestamp)
696187358Sjeff		return None
697139313Sjeff
698139313Sjeffclass PointEvent(Event):
699187358Sjeff	type = "point"
700187358Sjeff	def __init__(self, source, name, cpu, timestamp, attrs):
701187358Sjeff		Event.__init__(self, source, name, cpu, timestamp, attrs)
702139313Sjeff
703139313Sjeff	def draw(self, canvas, xpos, ypos):
704187358Sjeff		color = colormap.lookup(self.name)
705187379Sjeff		l = canvas.create_oval(xpos - XY_POINT, ypos,
706187379Sjeff		    xpos + XY_POINT, ypos - (XY_POINT * 2),
707187376Sjeff		    fill=color, width=0,
708187379Sjeff		    tags=("event", self.type, self.name, self.source.tag))
709187358Sjeff		Event.draw(self, canvas, xpos, ypos, l)
710139313Sjeff
711187358Sjeff		return xpos
712139313Sjeff
713139313Sjeffclass StateEvent(Event):
714187358Sjeff	type = "state"
715187358Sjeff	def __init__(self, source, name, cpu, timestamp, attrs):
716187358Sjeff		Event.__init__(self, source, name, cpu, timestamp, attrs)
717139313Sjeff
718139313Sjeff	def draw(self, canvas, xpos, ypos):
719187358Sjeff		next = self.nexttype("state")
720187358Sjeff		if (next == None):
721139313Sjeff			return (xpos)
722187376Sjeff		self.duration = duration = next.timestamp - self.timestamp
723187358Sjeff		self.attrs.insert(0, ("duration", ticks2sec(duration)))
724187358Sjeff		color = colormap.lookup(self.name)
725187358Sjeff		if (duration < 0):
726187358Sjeff			duration = 0
727166209Sjeff			print "Unsynchronized timestamp"
728166209Sjeff			print self.cpu, self.timestamp
729166209Sjeff			print next.cpu, next.timestamp
730187358Sjeff		delta = duration / canvas.ratio
731139313Sjeff		l = canvas.create_rectangle(xpos, ypos,
732187358Sjeff		    xpos + delta, ypos - 10, fill=color, width=0,
733187379Sjeff		    tags=("event", self.type, self.name, self.source.tag))
734187358Sjeff		Event.draw(self, canvas, xpos, ypos, l)
735139313Sjeff
736139313Sjeff		return (xpos + delta)
737139313Sjeff
738187358Sjeffclass CountEvent(Event):
739187358Sjeff	type = "count"
740187358Sjeff	def __init__(self, source, count, cpu, timestamp, attrs):
741187358Sjeff		count = int(count)
742187358Sjeff		self.count = count
743187358Sjeff		Event.__init__(self, source, "count", cpu, timestamp, attrs)
744139313Sjeff
745139313Sjeff	def draw(self, canvas, xpos, ypos):
746187358Sjeff		next = self.nexttype("count")
747187358Sjeff		if (next == None):
748187358Sjeff			return (xpos)
749187358Sjeff		color = colormap.lookup("count")
750187376Sjeff		self.duration = duration = next.timestamp - self.timestamp
751187471Sjeff		if (duration < 0):
752187471Sjeff			duration = 0
753187471Sjeff			print "Unsynchronized timestamp"
754187471Sjeff			print self.cpu, self.timestamp
755187471Sjeff			print next.cpu, next.timestamp
756187358Sjeff		self.attrs.insert(0, ("count", self.count))
757187358Sjeff		self.attrs.insert(1, ("duration", ticks2sec(duration)))
758187358Sjeff		delta = duration / canvas.ratio
759139313Sjeff		yhight = self.source.yscale() * self.count
760139313Sjeff		l = canvas.create_rectangle(xpos, ypos - yhight,
761187358Sjeff		    xpos + delta, ypos, fill=color, width=0,
762187379Sjeff		    tags=("event", self.type, self.name, self.source.tag))
763187358Sjeff		Event.draw(self, canvas, xpos, ypos, l)
764139313Sjeff		return (xpos + delta)
765139313Sjeff
766187358Sjeffclass PadEvent(StateEvent):
767187358Sjeff	type = "pad"
768187358Sjeff	def __init__(self, source, cpu, timestamp, last=0):
769187358Sjeff		if (last):
770187358Sjeff			cpu = source.events[len(source.events) -1].cpu
771187358Sjeff		else:
772187358Sjeff			cpu = source.events[0].cpu
773187358Sjeff		StateEvent.__init__(self, source, "pad", cpu, timestamp, [])
774139313Sjeff	def draw(self, canvas, xpos, ypos):
775139313Sjeff		next = self.next()
776139313Sjeff		if (next == None):
777139313Sjeff			return (xpos)
778187358Sjeff		duration = next.timestamp - self.timestamp
779187358Sjeff		delta = duration / canvas.ratio
780187358Sjeff		Event.draw(self, canvas, xpos, ypos, None)
781139313Sjeff		return (xpos + delta)
782139313Sjeff
783187376Sjeff# Sort function for start y address
784187376Sjeffdef source_cmp_start(x, y):
785187376Sjeff	return x.y - y.y
786187376Sjeff
787139313Sjeffclass EventSource:
788187358Sjeff	def __init__(self, group, id):
789187358Sjeff		self.name = id
790139313Sjeff		self.events = []
791187358Sjeff		self.cpuitems = []
792187155Sjhb		self.group = group
793187358Sjeff		self.y = 0
794187358Sjeff		self.item = None
795187359Sjeff		self.hidden = 0
796187376Sjeff		self.tag = group + id
797139313Sjeff
798187155Sjhb	def __cmp__(self, other):
799187358Sjeff		if (other == None):
800187358Sjeff			return -1
801187155Sjhb		if (self.group == other.group):
802187358Sjeff			return cmp(self.name, other.name)
803187155Sjhb		return cmp(self.group, other.group)
804187155Sjhb
805187155Sjhb	# It is much faster to append items to a list then to insert them
806187155Sjhb	# at the beginning.  As a result, we add events in reverse order
807187155Sjhb	# and then swap the list during fixup.
808139313Sjeff	def fixup(self):
809187155Sjhb		self.events.reverse()
810139313Sjeff
811187358Sjeff	def addevent(self, event):
812187155Sjhb		self.events.append(event)
813139313Sjeff
814187358Sjeff	def addlastevent(self, event):
815187155Sjhb		self.events.insert(0, event)
816139313Sjeff
817139313Sjeff	def draw(self, canvas, ypos):
818139313Sjeff		xpos = 10
819187358Sjeff		cpux = 10
820187358Sjeff		cpu = self.events[1].cpu
821139313Sjeff		for i in range(0, len(self.events)):
822139313Sjeff			self.events[i].idx = i
823139313Sjeff		for event in self.events:
824187358Sjeff			if (event.cpu != cpu and event.cpu != -1):
825187358Sjeff				self.drawcpu(canvas, cpu, cpux, xpos, ypos)
826187358Sjeff				cpux = xpos
827187358Sjeff				cpu = event.cpu
828139313Sjeff			xpos = event.draw(canvas, xpos, ypos)
829187358Sjeff		self.drawcpu(canvas, cpu, cpux, xpos, ypos)
830139313Sjeff
831139313Sjeff	def drawname(self, canvas, ypos):
832187358Sjeff		self.y = ypos
833139313Sjeff		ypos = ypos - (self.ysize() / 2)
834187379Sjeff		self.item = canvas.create_text(X_BORDER, ypos, anchor="w",
835187379Sjeff		    text=self.name)
836187358Sjeff		return (self.item)
837139313Sjeff
838187358Sjeff	def drawcpu(self, canvas, cpu, fromx, tox, ypos):
839187358Sjeff		cpu = "CPU " + str(cpu)
840187358Sjeff		color = cpucolormap.lookup(cpu)
841187358Sjeff		# Create the cpu background colors default to hidden
842187358Sjeff		l = canvas.create_rectangle(fromx,
843139313Sjeff		    ypos - self.ysize() - canvas.bdheight,
844187358Sjeff		    tox, ypos + canvas.bdheight, fill=color, width=0,
845187379Sjeff		    tags=("cpubg", cpu, self.tag), state="hidden")
846187358Sjeff		self.cpuitems.append(l)
847139313Sjeff
848187358Sjeff	def move(self, canvas, xpos, ypos):
849187376Sjeff		canvas.move(self.tag, xpos, ypos)
850187358Sjeff
851187358Sjeff	def movename(self, canvas, xpos, ypos):
852187358Sjeff		self.y += ypos
853187358Sjeff		canvas.move(self.item, xpos, ypos)
854187358Sjeff
855139313Sjeff	def ysize(self):
856187379Sjeff		return (Y_EVENTSOURCE)
857139313Sjeff
858139313Sjeff	def eventat(self, i):
859139313Sjeff		if (i >= len(self.events)):
860139313Sjeff			return (None)
861139313Sjeff		event = self.events[i]
862139313Sjeff		return (event)
863139313Sjeff
864139313Sjeff	def findevent(self, timestamp):
865139313Sjeff		for event in self.events:
866187358Sjeff			if (event.timestamp >= timestamp and event.type != "pad"):
867139313Sjeff				return (event)
868139313Sjeff		return (None)
869139313Sjeff
870187358Sjeffclass Counter(EventSource):
871187358Sjeff	#
872187358Sjeff	# Store a hash of counter groups that keeps the max value
873187358Sjeff	# for a counter in this group for scaling purposes.
874187358Sjeff	#
875187358Sjeff	groups = {}
876187358Sjeff	def __init__(self, group, id):
877139313Sjeff		try:
878187358Sjeff			Counter.cnt = Counter.groups[group]
879139313Sjeff		except:
880187358Sjeff			Counter.groups[group] = 0
881187358Sjeff		EventSource.__init__(self, group, id)
882139313Sjeff
883139313Sjeff	def fixup(self):
884187358Sjeff		for event in self.events:
885187358Sjeff			if (event.type != "count"):
886187358Sjeff				continue;
887187358Sjeff			count = int(event.count)
888187358Sjeff			if (count > Counter.groups[self.group]):
889187358Sjeff				Counter.groups[self.group] = count
890187155Sjhb		EventSource.fixup(self)
891139313Sjeff
892173743Ssam	def ymax(self):
893187358Sjeff		return (Counter.groups[self.group])
894173743Ssam
895139313Sjeff	def ysize(self):
896187379Sjeff		return (Y_COUNTER)
897139313Sjeff
898139313Sjeff	def yscale(self):
899187358Sjeff		return (self.ysize() / self.ymax())
900139313Sjeff
901139313Sjeffclass KTRFile:
902139313Sjeff	def __init__(self, file):
903166209Sjeff		self.timestamp_f = None
904166209Sjeff		self.timestamp_l = None
905187156Sjhb		self.locks = {}
906187156Sjhb		self.callwheels = {}
907139313Sjeff		self.ticks = {}
908139313Sjeff		self.load = {}
909152663Sscottl		self.crit = {}
910166209Sjeff		self.stathz = 0
911187379Sjeff		self.eventcnt = 0
912187471Sjeff		self.taghash = {}
913139313Sjeff
914139313Sjeff		self.parse(file)
915139313Sjeff		self.fixup()
916139313Sjeff		global ticksps
917139313Sjeff		ticksps = self.ticksps()
918187379Sjeff		span = self.timespan()
919187379Sjeff		ghz = float(ticksps) / 1000000000.0
920187379Sjeff		#
921187379Sjeff		# Update the title with some stats from the file
922187379Sjeff		#
923187379Sjeff		titlestr = "SchedGraph: "
924187379Sjeff		titlestr += ticks2sec(span) + " at %.3f ghz, " % ghz
925187379Sjeff		titlestr += str(len(sources)) + " event sources, "
926187379Sjeff		titlestr += str(self.eventcnt) + " events"
927187379Sjeff		root.title(titlestr)
928139313Sjeff
929139313Sjeff	def parse(self, file):
930139313Sjeff		try:
931139313Sjeff			ifp = open(file)
932139313Sjeff		except:
933139313Sjeff			print "Can't open", file
934139313Sjeff			sys.exit(1)
935139313Sjeff
936187358Sjeff		# quoteexp matches a quoted string, no escaping
937187358Sjeff		quoteexp = "\"([^\"]*)\""
938139313Sjeff
939187358Sjeff		#
940187358Sjeff		# commaexp matches a quoted string OR the string up
941187358Sjeff		# to the first ','
942187358Sjeff		#
943187358Sjeff		commaexp = "(?:" + quoteexp + "|([^,]+))"
944139313Sjeff
945187358Sjeff		#
946187358Sjeff		# colonstr matches a quoted string OR the string up
947187358Sjeff		# to the first ':'
948187358Sjeff		#
949187358Sjeff		colonexp = "(?:" + quoteexp + "|([^:]+))"
950139313Sjeff
951187358Sjeff		#
952187358Sjeff		# Match various manditory parts of the KTR string this is
953187358Sjeff		# fairly inflexible until you get to attributes to make
954187358Sjeff		# parsing faster.
955187358Sjeff		#
956187358Sjeff		hdrexp = "\s*(\d+)\s+(\d+)\s+(\d+)\s+"
957187358Sjeff		groupexp = "KTRGRAPH group:" + quoteexp + ", "
958187358Sjeff		idexp = "id:" + quoteexp + ", "
959187358Sjeff		typeexp = "([^:]+):" + commaexp + ", "
960187358Sjeff		attribexp = "attributes: (.*)"
961139313Sjeff
962187358Sjeff		#
963187358Sjeff		# Matches optional attributes in the KTR string.  This
964187358Sjeff		# tolerates more variance as the users supply these values.
965187358Sjeff		#
966187358Sjeff		attrexp = colonexp + "\s*:\s*(?:" + commaexp + ", (.*)|"
967187358Sjeff		attrexp += quoteexp +"|(.*))"
968139313Sjeff
969187358Sjeff		# Precompile regexp
970187358Sjeff		ktrre = re.compile(hdrexp + groupexp + idexp + typeexp + attribexp)
971187358Sjeff		attrre = re.compile(attrexp)
972139313Sjeff
973173743Ssam		global lineno
974173743Ssam		lineno = 0
975178571Sjeff		for line in ifp.readlines():
976173743Ssam			lineno += 1
977187358Sjeff			if ((lineno % 2048) == 0):
978173743Ssam				status.startup("Parsing line " + str(lineno))
979187358Sjeff			m = ktrre.match(line);
980173568Sjeff			if (m == None):
981187358Sjeff				print "Can't parse", lineno, line,
982187358Sjeff				continue;
983187358Sjeff			(index, cpu, timestamp, group, id, type, dat, dat1, attrstring) = m.groups();
984187358Sjeff			if (dat == None):
985187358Sjeff				dat = dat1
986187358Sjeff			if (self.checkstamp(timestamp) == 0):
987187471Sjeff				print "Bad timestamp at", lineno, ":",
988187471Sjeff				print cpu, timestamp
989187358Sjeff				continue
990187358Sjeff			#
991187358Sjeff			# Build the table of optional attributes
992187358Sjeff			#
993187358Sjeff			attrs = []
994187358Sjeff			while (attrstring != None):
995187358Sjeff				m = attrre.match(attrstring.strip())
996187358Sjeff				if (m == None):
997187358Sjeff					break;
998187358Sjeff				#
999187358Sjeff				# Name may or may not be quoted.
1000187358Sjeff				#
1001187358Sjeff				# For val we have four cases:
1002187358Sjeff				# 1) quotes followed by comma and more
1003187358Sjeff				#    attributes.
1004187358Sjeff				# 2) no quotes followed by comma and more
1005187358Sjeff				#    attributes.
1006187358Sjeff				# 3) no more attributes or comma with quotes.
1007187358Sjeff				# 4) no more attributes or comma without quotes.
1008187358Sjeff				#
1009187358Sjeff				(name, name1, val, val1, attrstring, end, end1) = m.groups();
1010187358Sjeff				if (name == None):
1011187358Sjeff					name = name1
1012187358Sjeff				if (end == None):
1013187358Sjeff					end = end1
1014187358Sjeff				if (val == None):
1015187358Sjeff					val = val1
1016187358Sjeff				if (val == None):
1017187358Sjeff					val = end
1018187358Sjeff				if (name == "stathz"):
1019187358Sjeff					self.setstathz(val, cpu)
1020187358Sjeff				attrs.append((name, val))
1021187358Sjeff			args = (dat, cpu, timestamp, attrs)
1022187358Sjeff			e = self.makeevent(group, id, type, args)
1023187358Sjeff			if (e == None):
1024187358Sjeff				print "Unknown type", type, lineno, line,
1025139313Sjeff
1026187358Sjeff	def makeevent(self, group, id, type, args):
1027187358Sjeff		e = None
1028187358Sjeff		source = self.makeid(group, id, type)
1029187358Sjeff		if (type == "state"):
1030187358Sjeff			e = StateEvent(source, *args)
1031187358Sjeff		elif (type == "counter"):
1032187358Sjeff			e = CountEvent(source, *args)
1033187358Sjeff		elif (type == "point"):
1034187358Sjeff			e = PointEvent(source, *args)
1035187358Sjeff		if (e != None):
1036187379Sjeff			self.eventcnt += 1
1037187358Sjeff			source.addevent(e);
1038187358Sjeff		return e
1039139313Sjeff
1040187358Sjeff	def setstathz(self, val, cpu):
1041187358Sjeff		self.stathz = int(val)
1042139313Sjeff		cpu = int(cpu)
1043139313Sjeff		try:
1044139313Sjeff			ticks = self.ticks[cpu]
1045139313Sjeff		except:
1046139313Sjeff			self.ticks[cpu] = 0
1047139313Sjeff		self.ticks[cpu] += 1
1048139313Sjeff
1049187358Sjeff	def checkstamp(self, timestamp):
1050187358Sjeff		timestamp = int(timestamp)
1051187358Sjeff		if (self.timestamp_f == None):
1052187358Sjeff			self.timestamp_f = timestamp;
1053187471Sjeff		if (self.timestamp_l != None and
1054187471Sjeff		    timestamp -2048> self.timestamp_l):
1055187358Sjeff			return (0)
1056187358Sjeff		self.timestamp_l = timestamp;
1057187358Sjeff		return (1)
1058139313Sjeff
1059187358Sjeff	def makeid(self, group, id, type):
1060187471Sjeff		tag = group + id
1061187471Sjeff		if (self.taghash.has_key(tag)):
1062187471Sjeff			return self.taghash[tag]
1063187358Sjeff		if (type == "counter"):
1064187358Sjeff			source = Counter(group, id)
1065187358Sjeff		else:
1066187358Sjeff			source = EventSource(group, id)
1067187358Sjeff		sources.append(source)
1068187471Sjeff		self.taghash[tag] = source
1069187358Sjeff		return (source)
1070139313Sjeff
1071187358Sjeff	def findid(self, id):
1072187358Sjeff		for source in sources:
1073187358Sjeff			if (source.name == id):
1074187358Sjeff				return source
1075187358Sjeff		return (None)
1076173568Sjeff
1077187358Sjeff	def timespan(self):
1078187358Sjeff		return (self.timestamp_f - self.timestamp_l);
1079139313Sjeff
1080187358Sjeff	def ticksps(self):
1081187358Sjeff		oneghz = 1000000000
1082187358Sjeff		# Use user supplied clock first
1083187358Sjeff		if (clockfreq != None):
1084187358Sjeff			return int(clockfreq * oneghz)
1085152663Sscottl
1086187358Sjeff		# Check for a discovered clock
1087187471Sjeff		if (self.stathz != 0):
1088187358Sjeff			return (self.timespan() / self.ticks[0]) * int(self.stathz)
1089187358Sjeff		# Pretend we have a 1ns clock
1090187358Sjeff		print "WARNING: No clock discovered and no frequency ",
1091187358Sjeff		print "specified via the command line."
1092187358Sjeff		print "Using fake 1ghz clock"
1093187358Sjeff		return (oneghz);
1094187156Sjhb
1095187358Sjeff	def fixup(self):
1096187358Sjeff		for source in sources:
1097187358Sjeff			e = PadEvent(source, -1, self.timestamp_l)
1098187358Sjeff			source.addevent(e)
1099187358Sjeff			e = PadEvent(source, -1, self.timestamp_f, last=1)
1100187358Sjeff			source.addlastevent(e)
1101187358Sjeff			source.fixup()
1102187358Sjeff		sources.sort()
1103187156Sjhb
1104187358Sjeffclass SchedNames(Canvas):
1105187358Sjeff	def __init__(self, master, display):
1106187358Sjeff		self.display = display
1107187358Sjeff		self.parent = master
1108187358Sjeff		self.bdheight = master.bdheight
1109187358Sjeff		self.items = {}
1110187358Sjeff		self.ysize = 0
1111187358Sjeff		self.lines = []
1112187358Sjeff		Canvas.__init__(self, master, width=120,
1113187358Sjeff		    height=display["height"], bg='grey',
1114187358Sjeff		    scrollregion=(0, 0, 50, 100))
1115187156Sjhb
1116187358Sjeff	def moveline(self, cur_y, y):
1117187358Sjeff		for line in self.lines:
1118187358Sjeff			(x0, y0, x1, y1) = self.coords(line)
1119187358Sjeff			if (cur_y != y0):
1120187358Sjeff				continue
1121187358Sjeff			self.move(line, 0, y)
1122187156Sjhb			return
1123187156Sjhb
1124187358Sjeff	def draw(self):
1125187358Sjeff		status.startup("Drawing names")
1126187358Sjeff		ypos = 0
1127187358Sjeff		self.configure(scrollregion=(0, 0,
1128187358Sjeff		    self["width"], self.display.ysize()))
1129187358Sjeff		for source in sources:
1130187358Sjeff			l = self.create_line(0, ypos, self["width"], ypos,
1131187358Sjeff			    width=1, fill="black", tags=("all","sources"))
1132187358Sjeff			self.lines.append(l)
1133187358Sjeff			ypos += self.bdheight
1134187358Sjeff			ypos += source.ysize()
1135187358Sjeff			t = source.drawname(self, ypos)
1136187358Sjeff			self.items[t] = source
1137187358Sjeff			ypos += self.bdheight
1138187358Sjeff		self.ysize = ypos
1139187358Sjeff		self.create_line(0, ypos, self["width"], ypos,
1140187358Sjeff		    width=1, fill="black", tags=("all",))
1141187358Sjeff		self.bind("<Button-1>", self.master.mousepress);
1142187376Sjeff		self.bind("<Button-3>", self.master.mousepressright);
1143187358Sjeff		self.bind("<ButtonRelease-1>", self.master.mouserelease);
1144187358Sjeff		self.bind("<B1-Motion>", self.master.mousemotion);
1145187156Sjhb
1146187359Sjeff	def updatescroll(self):
1147187359Sjeff		self.configure(scrollregion=(0, 0,
1148187359Sjeff		    self["width"], self.display.ysize()))
1149187156Sjhb
1150187359Sjeff
1151139313Sjeffclass SchedDisplay(Canvas):
1152139313Sjeff	def __init__(self, master):
1153187155Sjhb		self.ratio = 1
1154187155Sjhb		self.parent = master
1155187358Sjeff		self.bdheight = master.bdheight
1156187358Sjeff		self.items = {}
1157187358Sjeff		self.lines = []
1158139313Sjeff		Canvas.__init__(self, master, width=800, height=500, bg='grey',
1159139313Sjeff		     scrollregion=(0, 0, 800, 500))
1160139313Sjeff
1161187358Sjeff	def prepare(self):
1162187358Sjeff		#
1163187155Sjhb		# Compute a ratio to ensure that the file's timespan fits into
1164187155Sjhb		# 2^31.  Although python may handle larger values for X
1165187155Sjhb		# values, the Tk internals do not.
1166187358Sjeff		#
1167187155Sjhb		self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1
1168187155Sjhb
1169139313Sjeff	def draw(self):
1170139313Sjeff		ypos = 0
1171139313Sjeff		xsize = self.xsize()
1172187358Sjeff		for source in sources:
1173139313Sjeff			status.startup("Drawing " + source.name)
1174187358Sjeff			l = self.create_line(0, ypos, xsize, ypos,
1175139313Sjeff			    width=1, fill="black", tags=("all",))
1176187358Sjeff			self.lines.append(l)
1177139313Sjeff			ypos += self.bdheight
1178139313Sjeff			ypos += source.ysize()
1179139313Sjeff			source.draw(self, ypos)
1180139313Sjeff			ypos += self.bdheight
1181187379Sjeff		self.tag_raise("point", "state")
1182187379Sjeff		self.tag_lower("cpubg", ALL)
1183139313Sjeff		self.create_line(0, ypos, xsize, ypos,
1184187379Sjeff		    width=1, fill="black", tags=("lines",))
1185139313Sjeff		self.tag_bind("event", "<Enter>", self.mouseenter)
1186139313Sjeff		self.tag_bind("event", "<Leave>", self.mouseexit)
1187187358Sjeff		self.bind("<Button-1>", self.mousepress)
1188187376Sjeff		self.bind("<Button-3>", self.master.mousepressright);
1189187155Sjhb		self.bind("<Button-4>", self.wheelup)
1190187155Sjhb		self.bind("<Button-5>", self.wheeldown)
1191187358Sjeff		self.bind("<ButtonRelease-1>", self.master.mouserelease);
1192187358Sjeff		self.bind("<B1-Motion>", self.master.mousemotion);
1193139313Sjeff
1194187358Sjeff	def moveline(self, cur_y, y):
1195187358Sjeff		for line in self.lines:
1196187358Sjeff			(x0, y0, x1, y1) = self.coords(line)
1197187358Sjeff			if (cur_y != y0):
1198187358Sjeff				continue
1199187358Sjeff			self.move(line, 0, y)
1200187358Sjeff			return
1201187358Sjeff
1202139313Sjeff	def mouseenter(self, event):
1203139313Sjeff		item, = self.find_withtag(CURRENT)
1204187358Sjeff		self.items[item].mouseenter(self)
1205139313Sjeff
1206139313Sjeff	def mouseexit(self, event):
1207139313Sjeff		item, = self.find_withtag(CURRENT)
1208187358Sjeff		self.items[item].mouseexit(self)
1209139313Sjeff
1210139313Sjeff	def mousepress(self, event):
1211187358Sjeff		# Find out what's beneath us
1212187358Sjeff		items = self.find_withtag(CURRENT)
1213187358Sjeff		if (len(items) == 0):
1214187358Sjeff			self.master.mousepress(event)
1215187358Sjeff			return
1216187358Sjeff		# Only grab mouse presses for things with event tags.
1217187358Sjeff		item = items[0]
1218187358Sjeff		tags = self.gettags(item)
1219187358Sjeff		for tag in tags:
1220187358Sjeff			if (tag == "event"):
1221187358Sjeff				self.items[item].mousepress(self)
1222187358Sjeff				return
1223187358Sjeff		# Leave the rest to the master window
1224187358Sjeff		self.master.mousepress(event)
1225139313Sjeff
1226187155Sjhb	def wheeldown(self, event):
1227187155Sjhb		self.parent.display_yview("scroll", 1, "units")
1228187155Sjhb
1229187155Sjhb	def wheelup(self, event):
1230187155Sjhb		self.parent.display_yview("scroll", -1, "units")
1231187155Sjhb
1232139313Sjeff	def xsize(self):
1233187379Sjeff		return ((ktrfile.timespan() / self.ratio) + (X_BORDER * 2))
1234139313Sjeff
1235139313Sjeff	def ysize(self):
1236139313Sjeff		ysize = 0
1237187358Sjeff		for source in sources:
1238187359Sjeff			if (source.hidden == 1):
1239187359Sjeff				continue
1240187359Sjeff			ysize += self.parent.sourcesize(source)
1241187358Sjeff		return ysize
1242139313Sjeff
1243139313Sjeff	def scaleset(self, ratio):
1244187358Sjeff		if (ktrfile == None):
1245139313Sjeff			return
1246139313Sjeff		oldratio = self.ratio
1247187358Sjeff		xstart, xend = self.xview()
1248187358Sjeff		midpoint = xstart + ((xend - xstart) / 2)
1249139313Sjeff
1250139313Sjeff		self.ratio = ratio
1251187359Sjeff		self.updatescroll()
1252187379Sjeff		self.scale(ALL, 0, 0, float(oldratio) / ratio, 1)
1253139313Sjeff
1254187358Sjeff		xstart, xend = self.xview()
1255187358Sjeff		xsize = (xend - xstart) / 2
1256187358Sjeff		self.xview_moveto(midpoint - xsize)
1257139313Sjeff
1258187359Sjeff	def updatescroll(self):
1259187359Sjeff		self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
1260187359Sjeff
1261139313Sjeff	def scaleget(self):
1262139313Sjeff		return self.ratio
1263139313Sjeff
1264187358Sjeff	def getcolor(self, tag):
1265187358Sjeff		return self.itemcget(tag, "fill")
1266187358Sjeff
1267187358Sjeff	def getstate(self, tag):
1268187358Sjeff		return self.itemcget(tag, "state")
1269187358Sjeff
1270139313Sjeff	def setcolor(self, tag, color):
1271139313Sjeff		self.itemconfigure(tag, state="normal", fill=color)
1272139313Sjeff
1273139313Sjeff	def hide(self, tag):
1274139313Sjeff		self.itemconfigure(tag, state="hidden")
1275139313Sjeff
1276139313Sjeffclass GraphMenu(Frame):
1277139313Sjeff	def __init__(self, master):
1278139313Sjeff		Frame.__init__(self, master, bd=2, relief=RAISED)
1279187376Sjeff		self.conf = Menubutton(self, text="Configure")
1280187376Sjeff		self.confmenu = Menu(self.conf, tearoff=0)
1281187376Sjeff		self.confmenu.add_command(label="Event Colors",
1282139313Sjeff		    command=self.econf)
1283187376Sjeff		self.confmenu.add_command(label="CPU Colors",
1284187358Sjeff		    command=self.cconf)
1285187376Sjeff		self.confmenu.add_command(label="Source Configure",
1286187359Sjeff		    command=self.sconf)
1287187376Sjeff		self.conf["menu"] = self.confmenu
1288187376Sjeff		self.conf.pack(side=LEFT)
1289139313Sjeff
1290139313Sjeff	def econf(self):
1291187358Sjeff		ColorConfigure(eventcolors, "Event Display Configuration")
1292139313Sjeff
1293187358Sjeff	def cconf(self):
1294187358Sjeff		ColorConfigure(cpucolors, "CPU Background Colors")
1295139313Sjeff
1296187359Sjeff	def sconf(self):
1297187359Sjeff		SourceConfigure()
1298187358Sjeff
1299139313Sjeffclass SchedGraph(Frame):
1300139313Sjeff	def __init__(self, master):
1301139313Sjeff		Frame.__init__(self, master)
1302139313Sjeff		self.menu = None
1303139313Sjeff		self.names = None
1304139313Sjeff		self.display = None
1305139313Sjeff		self.scale = None
1306139313Sjeff		self.status = None
1307187379Sjeff		self.bdheight = Y_BORDER
1308187358Sjeff		self.clicksource = None
1309187358Sjeff		self.lastsource = None
1310139313Sjeff		self.pack(expand=1, fill="both")
1311139313Sjeff		self.buildwidgets()
1312139313Sjeff		self.layout()
1313139313Sjeff
1314139313Sjeff	def buildwidgets(self):
1315139313Sjeff		global status
1316139313Sjeff		self.menu = GraphMenu(self)
1317139313Sjeff		self.display = SchedDisplay(self)
1318187358Sjeff		self.names = SchedNames(self, self.display)
1319139313Sjeff		self.scale = Scaler(self, self.display)
1320139313Sjeff		status = self.status = Status(self)
1321139313Sjeff		self.scrollY = Scrollbar(self, orient="vertical",
1322139313Sjeff		    command=self.display_yview)
1323139313Sjeff		self.display.scrollX = Scrollbar(self, orient="horizontal",
1324139313Sjeff		    command=self.display.xview)
1325139313Sjeff		self.display["xscrollcommand"] = self.display.scrollX.set
1326139313Sjeff		self.display["yscrollcommand"] = self.scrollY.set
1327139313Sjeff		self.names["yscrollcommand"] = self.scrollY.set
1328139313Sjeff
1329139313Sjeff	def layout(self):
1330139313Sjeff		self.columnconfigure(1, weight=1)
1331139313Sjeff		self.rowconfigure(1, weight=1)
1332139313Sjeff		self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1333139313Sjeff		self.names.grid(row=1, column=0, sticky=N+S)
1334139313Sjeff		self.display.grid(row=1, column=1, sticky=W+E+N+S)
1335139313Sjeff		self.scrollY.grid(row=1, column=2, sticky=N+S)
1336139313Sjeff		self.display.scrollX.grid(row=2, column=0, columnspan=2,
1337139313Sjeff		    sticky=E+W)
1338139313Sjeff		self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1339139313Sjeff		self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1340139313Sjeff
1341187358Sjeff	def draw(self):
1342139313Sjeff		self.master.update()
1343187358Sjeff		self.display.prepare()
1344187358Sjeff		self.names.draw()
1345139313Sjeff		self.display.draw()
1346187358Sjeff		self.status.startup("")
1347187471Sjeff		#
1348187471Sjeff		# Configure scale related values
1349187471Sjeff		#
1350187471Sjeff		scalemax = ktrfile.timespan() / int(self.display["width"])
1351187471Sjeff		width = int(root.geometry().split('x')[0])
1352187471Sjeff		self.constwidth = width - int(self.display["width"])
1353187471Sjeff		self.scale.setmax(scalemax)
1354187471Sjeff		self.scale.set(scalemax)
1355139313Sjeff		self.display.xview_moveto(0)
1356187471Sjeff		self.bind("<Configure>", self.resize)
1357139313Sjeff
1358187358Sjeff	def mousepress(self, event):
1359187358Sjeff		self.clicksource = self.sourceat(event.y)
1360187358Sjeff
1361187376Sjeff	def mousepressright(self, event):
1362187376Sjeff		source = self.sourceat(event.y)
1363187376Sjeff		if (source == None):
1364187376Sjeff			return
1365187376Sjeff		SourceContext(event, source)
1366187376Sjeff
1367187358Sjeff	def mouserelease(self, event):
1368187358Sjeff		if (self.clicksource == None):
1369187358Sjeff			return
1370187358Sjeff		newsource = self.sourceat(event.y)
1371187358Sjeff		if (self.clicksource != newsource):
1372187358Sjeff			self.sourceswap(self.clicksource, newsource)
1373187358Sjeff		self.clicksource = None
1374187358Sjeff		self.lastsource = None
1375187358Sjeff
1376187358Sjeff	def mousemotion(self, event):
1377187358Sjeff		if (self.clicksource == None):
1378187358Sjeff			return
1379187358Sjeff		newsource = self.sourceat(event.y)
1380187358Sjeff		#
1381187358Sjeff		# If we get a None source they moved off the page.
1382187358Sjeff		# swapsource() can't handle moving multiple items so just
1383187358Sjeff		# pretend we never clicked on anything to begin with so the
1384187358Sjeff		# user can't mouseover a non-contiguous area.
1385187358Sjeff		#
1386187358Sjeff		if (newsource == None):
1387187358Sjeff			self.clicksource = None
1388187358Sjeff			self.lastsource = None
1389187358Sjeff			return
1390187358Sjeff		if (newsource == self.lastsource):
1391187358Sjeff			return;
1392187358Sjeff		self.lastsource = newsource
1393187358Sjeff		if (newsource != self.clicksource):
1394187358Sjeff			self.sourceswap(self.clicksource, newsource)
1395187358Sjeff
1396187358Sjeff	# These are here because this object controls layout
1397187358Sjeff	def sourcestart(self, source):
1398187358Sjeff		return source.y - self.bdheight - source.ysize()
1399187358Sjeff
1400187358Sjeff	def sourceend(self, source):
1401187358Sjeff		return source.y + self.bdheight
1402187358Sjeff
1403187358Sjeff	def sourcesize(self, source):
1404187358Sjeff		return (self.bdheight * 2) + source.ysize()
1405187358Sjeff
1406187358Sjeff	def sourceswap(self, source1, source2):
1407187358Sjeff		# Sort so we always know which one is on top.
1408187358Sjeff		if (source2.y < source1.y):
1409187358Sjeff			swap = source1
1410187358Sjeff			source1 = source2
1411187358Sjeff			source2 = swap
1412187358Sjeff		# Only swap adjacent sources
1413187358Sjeff		if (self.sourceend(source1) != self.sourcestart(source2)):
1414187358Sjeff			return
1415187358Sjeff		# Compute start coordinates and target coordinates
1416187358Sjeff		y1 = self.sourcestart(source1)
1417187358Sjeff		y2 = self.sourcestart(source2)
1418187358Sjeff		y1targ = y1 + self.sourcesize(source2)
1419187358Sjeff		y2targ = y1
1420187358Sjeff		#
1421187358Sjeff		# If the sizes are not equal, adjust the start of the lower
1422187358Sjeff		# source to account for the lost/gained space.
1423187358Sjeff		#
1424187358Sjeff		if (source1.ysize() != source2.ysize()):
1425187358Sjeff			diff = source2.ysize() - source1.ysize()
1426187358Sjeff			self.names.moveline(y2, diff);
1427187358Sjeff			self.display.moveline(y2, diff)
1428187358Sjeff		source1.move(self.display, 0, y1targ - y1)
1429187358Sjeff		source2.move(self.display, 0, y2targ - y2)
1430187358Sjeff		source1.movename(self.names, 0, y1targ - y1)
1431187358Sjeff		source2.movename(self.names, 0, y2targ - y2)
1432187358Sjeff
1433187376Sjeff	def sourcepicky(self, source):
1434187359Sjeff		if (source.hidden == 0):
1435187376Sjeff			return self.sourcestart(source)
1436187376Sjeff		# Revert to group based sort
1437187376Sjeff		sources.sort()
1438187359Sjeff		prev = None
1439187359Sjeff		for s in sources:
1440187359Sjeff			if (s == source):
1441187359Sjeff				break
1442187359Sjeff			if (s.hidden == 0):
1443187359Sjeff				prev = s
1444187359Sjeff		if (prev == None):
1445187359Sjeff			newy = 0
1446187359Sjeff		else:
1447187359Sjeff			newy = self.sourcestart(prev) + self.sourcesize(prev)
1448187376Sjeff		return newy
1449187376Sjeff
1450187376Sjeff	def sourceshow(self, source):
1451187376Sjeff		if (source.hidden == 0):
1452187376Sjeff			return;
1453187376Sjeff		newy = self.sourcepicky(source)
1454187376Sjeff		off = newy - self.sourcestart(source)
1455187359Sjeff		self.sourceshiftall(newy-1, self.sourcesize(source))
1456187359Sjeff		self.sourceshift(source, off)
1457187359Sjeff		source.hidden = 0
1458187359Sjeff
1459187376Sjeff	#
1460187376Sjeff	# Optimized source show of multiple entries that only moves each
1461187376Sjeff	# existing entry once.  Doing sourceshow() iteratively is too
1462187376Sjeff	# expensive due to python's canvas.move().
1463187376Sjeff	#
1464187376Sjeff	def sourceshowlist(self, srclist):
1465187376Sjeff		srclist.sort(cmp=source_cmp_start)
1466187376Sjeff		startsize = []
1467187376Sjeff		for source in srclist:
1468187376Sjeff			if (source.hidden == 0):
1469187376Sjeff				srclist.remove(source)
1470187376Sjeff			startsize.append((self.sourcepicky(source),
1471187376Sjeff			    self.sourcesize(source)))
1472187376Sjeff
1473187376Sjeff		sources.sort(cmp=source_cmp_start, reverse=True)
1474187376Sjeff		self.status.startup("Updating display...");
1475187376Sjeff		for source in sources:
1476187376Sjeff			if (source.hidden == 1):
1477187376Sjeff				continue
1478187376Sjeff			nstart = self.sourcestart(source)
1479187376Sjeff			size = 0
1480187376Sjeff			for hidden in startsize:
1481187376Sjeff				(start, sz) = hidden
1482187376Sjeff				if (start <= nstart or start+sz <= nstart):
1483187376Sjeff					size += sz
1484187376Sjeff			self.sourceshift(source, size)
1485187376Sjeff		idx = 0
1486187376Sjeff		size = 0
1487187376Sjeff		for source in srclist:
1488187376Sjeff			(newy, sz) = startsize[idx]
1489187376Sjeff			off = (newy + size) - self.sourcestart(source)
1490187376Sjeff			self.sourceshift(source, off)
1491187376Sjeff			source.hidden = 0
1492187376Sjeff			size += sz
1493187376Sjeff			idx += 1
1494187471Sjeff		self.updatescroll()
1495187376Sjeff		self.status.set("")
1496187376Sjeff
1497187376Sjeff	#
1498187376Sjeff	# Optimized source hide of multiple entries that only moves each
1499187376Sjeff	# remaining entry once.  Doing sourcehide() iteratively is too
1500187376Sjeff	# expensive due to python's canvas.move().
1501187376Sjeff	#
1502187376Sjeff	def sourcehidelist(self, srclist):
1503187376Sjeff		srclist.sort(cmp=source_cmp_start)
1504187376Sjeff		sources.sort(cmp=source_cmp_start)
1505187376Sjeff		startsize = []
1506187376Sjeff		off = len(sources) * 100
1507187376Sjeff		self.status.startup("Updating display...");
1508187376Sjeff		for source in srclist:
1509187376Sjeff			if (source.hidden == 1):
1510187376Sjeff				srclist.remove(source)
1511187376Sjeff			#
1512187376Sjeff			# Remember our old position so we can sort things
1513187376Sjeff			# below us when we're done.
1514187376Sjeff			#
1515187376Sjeff			startsize.append((self.sourcestart(source),
1516187376Sjeff			    self.sourcesize(source)))
1517187376Sjeff			self.sourceshift(source, off)
1518187376Sjeff			source.hidden = 1
1519187376Sjeff
1520187376Sjeff		idx = 0
1521187376Sjeff		size = 0
1522187376Sjeff		for hidden in startsize:
1523187376Sjeff			(start, sz) = hidden
1524187376Sjeff			size += sz
1525187376Sjeff			if (idx + 1 < len(startsize)):
1526187376Sjeff				(stop, sz) = startsize[idx+1]
1527187376Sjeff			else:
1528187376Sjeff				stop = self.display.ysize()
1529187376Sjeff			idx += 1
1530187376Sjeff			for source in sources:
1531187376Sjeff				nstart = self.sourcestart(source)
1532187376Sjeff				if (nstart < start or source.hidden == 1):
1533187376Sjeff					continue
1534187376Sjeff				if (nstart >= stop):
1535187376Sjeff					break;
1536187376Sjeff				self.sourceshift(source, -size)
1537187471Sjeff		self.updatescroll()
1538187376Sjeff		self.status.set("")
1539187376Sjeff
1540187359Sjeff	def sourcehide(self, source):
1541187376Sjeff		if (source.hidden == 1):
1542187376Sjeff			return;
1543187359Sjeff		# Move it out of the visible area
1544187359Sjeff		off = len(sources) * 100
1545187359Sjeff		start = self.sourcestart(source)
1546187359Sjeff		self.sourceshift(source, off)
1547187359Sjeff		self.sourceshiftall(start, -self.sourcesize(source))
1548187359Sjeff		source.hidden = 1
1549187359Sjeff
1550187359Sjeff	def sourceshift(self, source, off):
1551187359Sjeff		start = self.sourcestart(source)
1552187359Sjeff		source.move(self.display, 0, off)
1553187359Sjeff		source.movename(self.names, 0, off)
1554187359Sjeff		self.names.moveline(start, off);
1555187359Sjeff		self.display.moveline(start, off)
1556187376Sjeff		#
1557187376Sjeff		# We update the idle tasks to shrink the dirtied area so
1558187376Sjeff		# it does not always include the entire screen.
1559187376Sjeff		#
1560187376Sjeff		self.names.update_idletasks()
1561187376Sjeff		self.display.update_idletasks()
1562187359Sjeff
1563187359Sjeff	def sourceshiftall(self, start, off):
1564187376Sjeff		self.status.startup("Updating display...");
1565187359Sjeff		for source in sources:
1566187359Sjeff			nstart = self.sourcestart(source)
1567187359Sjeff			if (nstart < start):
1568187359Sjeff				continue;
1569187359Sjeff			self.sourceshift(source, off)
1570187471Sjeff		self.updatescroll()
1571187376Sjeff		self.status.set("")
1572187359Sjeff
1573187358Sjeff	def sourceat(self, ypos):
1574187358Sjeff		(start, end) = self.names.yview()
1575187358Sjeff		starty = start * float(self.names.ysize)
1576187358Sjeff		ypos += starty
1577187358Sjeff		for source in sources:
1578187359Sjeff			if (source.hidden == 1):
1579187359Sjeff				continue;
1580187358Sjeff			yend = self.sourceend(source)
1581187358Sjeff			ystart = self.sourcestart(source)
1582187358Sjeff			if (ypos >= ystart and ypos <= yend):
1583187358Sjeff				return source
1584187358Sjeff		return None
1585187358Sjeff
1586139313Sjeff	def display_yview(self, *args):
1587139313Sjeff		self.names.yview(*args)
1588139313Sjeff		self.display.yview(*args)
1589139313Sjeff
1590187471Sjeff	def resize(self, *args):
1591187471Sjeff		width = int(root.geometry().split('x')[0])
1592187471Sjeff		scalemax = ktrfile.timespan() / (width - self.constwidth)
1593187471Sjeff		self.scale.setmax(scalemax)
1594187471Sjeff
1595187471Sjeff	def updatescroll(self):
1596187471Sjeff		self.names.updatescroll()
1597187471Sjeff		self.display.updatescroll()
1598187471Sjeff
1599139313Sjeff	def setcolor(self, tag, color):
1600139313Sjeff		self.display.setcolor(tag, color)
1601139313Sjeff
1602139313Sjeff	def hide(self, tag):
1603139313Sjeff		self.display.hide(tag)
1604139313Sjeff
1605187358Sjeff	def getcolor(self, tag):
1606187358Sjeff		return self.display.getcolor(tag)
1607187358Sjeff
1608187358Sjeff	def getstate(self, tag):
1609187358Sjeff		return self.display.getstate(tag)
1610187358Sjeff
1611187358Sjeffif (len(sys.argv) != 2 and len(sys.argv) != 3):
1612187358Sjeff	print "usage:", sys.argv[0], "<ktr file> [clock freq in ghz]"
1613139313Sjeff	sys.exit(1)
1614139313Sjeff
1615187358Sjeffif (len(sys.argv) > 2):
1616187358Sjeff	clockfreq = float(sys.argv[2])
1617187358Sjeff
1618139313Sjeffroot = Tk()
1619187379Sjeffroot.title("SchedGraph")
1620187358Sjeffcolormap = Colormap(eventcolors)
1621187358Sjeffcpucolormap = Colormap(cpucolors)
1622139313Sjeffgraph = SchedGraph(root)
1623187358Sjeffktrfile = KTRFile(sys.argv[1])
1624187358Sjeffgraph.draw()
1625139313Sjeffroot.mainloop()
1626