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