xref: /freebsd/tools/sched/schedgraph.py (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
1dcee3bd3SJeff Roberson#!/usr/local/bin/python
2dcee3bd3SJeff Roberson
3b62baf95SJeff Roberson# Copyright (c) 2002-2003, 2009, Jeffrey Roberson <jeff@freebsd.org>
4dcee3bd3SJeff Roberson# All rights reserved.
5dcee3bd3SJeff Roberson#
6dcee3bd3SJeff Roberson# Redistribution and use in source and binary forms, with or without
7dcee3bd3SJeff Roberson# modification, are permitted provided that the following conditions
8dcee3bd3SJeff Roberson# are met:
9dcee3bd3SJeff Roberson# 1. Redistributions of source code must retain the above copyright
10dcee3bd3SJeff Roberson#    notice unmodified, this list of conditions, and the following
11dcee3bd3SJeff Roberson#    disclaimer.
12dcee3bd3SJeff Roberson# 2. Redistributions in binary form must reproduce the above copyright
13dcee3bd3SJeff Roberson#    notice, this list of conditions and the following disclaimer in the
14dcee3bd3SJeff Roberson#     documentation and/or other materials provided with the distribution.
15dcee3bd3SJeff Roberson#
16dcee3bd3SJeff Roberson# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17dcee3bd3SJeff Roberson# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18dcee3bd3SJeff Roberson# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19dcee3bd3SJeff Roberson# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20dcee3bd3SJeff Roberson# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21dcee3bd3SJeff Roberson# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22dcee3bd3SJeff Roberson# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23dcee3bd3SJeff Roberson# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24dcee3bd3SJeff Roberson# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25dcee3bd3SJeff Roberson# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26dcee3bd3SJeff Roberson#
27dcee3bd3SJeff Roberson
28749f65e3SCraig Rodriguesfrom __future__ import print_function
29dcee3bd3SJeff Robersonimport sys
30dcee3bd3SJeff Robersonimport re
31ec5cae07SJeff Robersonimport random
32*7e8ed296SAndriy Gaponfrom operator import attrgetter, itemgetter
33*7e8ed296SAndriy Gaponfrom functools import total_ordering
34*7e8ed296SAndriy Gaponfrom tkinter import *
35dcee3bd3SJeff Roberson
36698e6141SAndrew R. Reiter# To use:
3766835de4SSam Leffler# - Install the ports/x11-toolkits/py-tkinter package; e.g.
38*7e8ed296SAndriy Gapon#	pkg install x11-toolkits/py-tkinter
3966835de4SSam Leffler# - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF; e.g.
4066835de4SSam Leffler#	options 	KTR
4166835de4SSam Leffler#	options 	KTR_ENTRIES=32768
4266835de4SSam Leffler#	options 	KTR_COMPILE=(KTR_SCHED)
4366835de4SSam Leffler#	options 	KTR_MASK=(KTR_SCHED)
4466835de4SSam Leffler# - It is encouraged to increase KTR_ENTRIES size to gather enough
4566835de4SSam Leffler#    information for analysis; e.g.
4666835de4SSam Leffler#	options 	KTR_ENTRIES=262144
47e73e7730SKris Kennaway#   as 32768 entries may only correspond to a second or two of profiling
48e73e7730SKris Kennaway#   data depending on your workload.
4970015002SKris Kennaway# - Rebuild kernel with proper changes to KERNCONF and boot new kernel.
5070015002SKris Kennaway# - Run your workload to be profiled.
5170015002SKris Kennaway# - While the workload is continuing (i.e. before it finishes), disable
5270015002SKris Kennaway#   KTR tracing by setting 'sysctl debug.ktr.mask=0'.  This is necessary
5370015002SKris Kennaway#   to avoid a race condition while running ktrdump, i.e. the KTR ring buffer
5470015002SKris Kennaway#   will cycle a bit while ktrdump runs, and this confuses schedgraph because
5570015002SKris Kennaway#   the timestamps appear to go backwards at some point.  Stopping KTR logging
5670015002SKris Kennaway#   while the workload is still running is to avoid wasting log entries on
5770015002SKris Kennaway#   "idle" time at the end.
58698e6141SAndrew R. Reiter# - Dump the trace to a file: 'ktrdump -ct > ktr.out'
59*7e8ed296SAndriy Gapon# - Alternatively, use schedgraph.d script in this directory for getting
60*7e8ed296SAndriy Gapon#   the trace data by means of DTrace.  See the script for details.
61ec5cae07SJeff Roberson# - Run the python script: 'python schedgraph.py ktr.out' optionally provide
62ec5cae07SJeff Roberson#   your cpu frequency in ghz: 'python schedgraph.py ktr.out 2.4'
63698e6141SAndrew R. Reiter#
64698e6141SAndrew R. Reiter# To do:
65ec5cae07SJeff Roberson# Add a per-source summary display
66ec5cae07SJeff Roberson# "Vertical rule" to help relate data in different rows
67ec5cae07SJeff Roberson# Mouse-over popup of full thread/event/row label (currently truncated)
68ec5cae07SJeff Roberson# More visible anchors for popup event windows
6970015002SKris Kennaway#
7070015002SKris Kennaway# BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of
7170015002SKris Kennaway#          colours to represent them ;-)
72ec5cae07SJeff Roberson
73ec5cae07SJeff Robersoneventcolors = [
74ec5cae07SJeff Roberson	("count",	"red"),
75ec5cae07SJeff Roberson	("running",	"green"),
76ec5cae07SJeff Roberson	("idle",	"grey"),
772cba8dd3SJohn Baldwin	("spinning",	"red"),
78ec5cae07SJeff Roberson	("yielding",	"yellow"),
79ec5cae07SJeff Roberson	("swapped",	"violet"),
80ec5cae07SJeff Roberson	("suspended",	"purple"),
81ec5cae07SJeff Roberson	("iwait",	"grey"),
82ec5cae07SJeff Roberson	("sleep",	"blue"),
83ec5cae07SJeff Roberson	("blocked",	"dark red"),
84ec5cae07SJeff Roberson	("runq add",	"yellow"),
85ec5cae07SJeff Roberson	("runq rem",	"yellow"),
86ec5cae07SJeff Roberson	("thread exit",	"grey"),
87ec5cae07SJeff Roberson	("proc exit",	"grey"),
88ec5cae07SJeff Roberson	("lock acquire", "blue"),
89ec5cae07SJeff Roberson	("lock contest", "purple"),
90ec5cae07SJeff Roberson	("failed lock try", "red"),
91ec5cae07SJeff Roberson	("lock release", "grey"),
92932f0fa2SJeff Roberson	("statclock",	"black"),
93ec5cae07SJeff Roberson	("prio",	"black"),
94ec5cae07SJeff Roberson	("lend prio",	"black"),
95ec5cae07SJeff Roberson	("wokeup",	"black")
96ec5cae07SJeff Roberson]
97ec5cae07SJeff Roberson
98ec5cae07SJeff Robersoncpucolors = [
99ec5cae07SJeff Roberson	("CPU 0",	"light grey"),
100ec5cae07SJeff Roberson	("CPU 1",	"dark grey"),
101ec5cae07SJeff Roberson	("CPU 2",	"light blue"),
102ec5cae07SJeff Roberson	("CPU 3",	"light pink"),
103ec5cae07SJeff Roberson	("CPU 4",	"blanched almond"),
104ec5cae07SJeff Roberson	("CPU 5",	"slate grey"),
105ec5cae07SJeff Roberson	("CPU 6",	"tan"),
106ec5cae07SJeff Roberson	("CPU 7",	"thistle"),
107ec5cae07SJeff Roberson	("CPU 8",	"white")
108ec5cae07SJeff Roberson]
109ec5cae07SJeff Roberson
110ec5cae07SJeff Robersoncolors = [
111ec5cae07SJeff Roberson	"white", "thistle", "blanched almond", "tan", "chartreuse",
112ec5cae07SJeff Roberson	"dark red", "red", "pale violet red", "pink", "light pink",
113ec5cae07SJeff Roberson	"dark orange", "orange", "coral", "light coral",
114ec5cae07SJeff Roberson	"goldenrod", "gold", "yellow", "light yellow",
115ec5cae07SJeff Roberson	"dark green", "green", "light green", "light sea green",
116ec5cae07SJeff Roberson	"dark blue", "blue", "light blue", "steel blue", "light slate blue",
117ec5cae07SJeff Roberson	"dark violet", "violet", "purple", "blue violet",
118ec5cae07SJeff Roberson	"dark grey", "slate grey", "light grey",
119ec5cae07SJeff Roberson	"black",
120ec5cae07SJeff Roberson]
121ec5cae07SJeff Robersoncolors.sort()
122dcee3bd3SJeff Roberson
123dcee3bd3SJeff Robersonticksps = None
124dcee3bd3SJeff Robersonstatus = None
125ec5cae07SJeff Robersoncolormap = None
126ec5cae07SJeff Robersonktrfile = None
127ec5cae07SJeff Robersonclockfreq = None
128ec5cae07SJeff Robersonsources = []
12966835de4SSam Lefflerlineno = -1
130dcee3bd3SJeff Roberson
131932f0fa2SJeff RobersonY_BORDER = 10
132932f0fa2SJeff RobersonX_BORDER = 10
133932f0fa2SJeff RobersonY_COUNTER = 80
134932f0fa2SJeff RobersonY_EVENTSOURCE = 10
135932f0fa2SJeff RobersonXY_POINT = 4
136932f0fa2SJeff Roberson
137ec5cae07SJeff Robersonclass Colormap:
138ec5cae07SJeff Roberson	def __init__(self, table):
139ec5cae07SJeff Roberson		self.table = table
140ec5cae07SJeff Roberson		self.map = {}
141ec5cae07SJeff Roberson		for entry in table:
142ec5cae07SJeff Roberson			self.map[entry[0]] = entry[1]
143ec5cae07SJeff Roberson
144ec5cae07SJeff Roberson	def lookup(self, name):
145ec5cae07SJeff Roberson		try:
146ec5cae07SJeff Roberson			color = self.map[name]
147ec5cae07SJeff Roberson		except:
148ec5cae07SJeff Roberson			color = colors[random.randrange(0, len(colors))]
149749f65e3SCraig Rodrigues			print("Picking random color", color, "for", name)
150ec5cae07SJeff Roberson			self.map[name] = color
151ec5cae07SJeff Roberson			self.table.append((name, color))
152ec5cae07SJeff Roberson		return (color)
153ec5cae07SJeff Roberson
154dcee3bd3SJeff Robersondef ticks2sec(ticks):
155b62baf95SJeff Roberson	ticks = float(ticks)
156b62baf95SJeff Roberson	ns = float(ticksps) / 1000000000
157b62baf95SJeff Roberson	ticks /= ns
158dcee3bd3SJeff Roberson	if (ticks < 1000):
159b62baf95SJeff Roberson		return ("%.2fns" % ticks)
160dcee3bd3SJeff Roberson	ticks /= 1000
161dcee3bd3SJeff Roberson	if (ticks < 1000):
162b62baf95SJeff Roberson		return ("%.2fus" % ticks)
163dcee3bd3SJeff Roberson	ticks /= 1000
164b62baf95SJeff Roberson	if (ticks < 1000):
165b62baf95SJeff Roberson		return ("%.2fms" % ticks)
166b62baf95SJeff Roberson	ticks /= 1000
167b62baf95SJeff Roberson	return ("%.2fs" % ticks)
168dcee3bd3SJeff Roberson
169dcee3bd3SJeff Robersonclass Scaler(Frame):
170dcee3bd3SJeff Roberson	def __init__(self, master, target):
171dcee3bd3SJeff Roberson		Frame.__init__(self, master)
17216ef0f3bSJeff Roberson		self.scale = None
17316ef0f3bSJeff Roberson		self.target = target
174dcee3bd3SJeff Roberson		self.label = Label(self, text="Ticks per pixel")
175dcee3bd3SJeff Roberson		self.label.pack(side=LEFT)
17616ef0f3bSJeff Roberson		self.resolution = 100
17716ef0f3bSJeff Roberson		self.setmax(10000)
178dcee3bd3SJeff Roberson
179dcee3bd3SJeff Roberson	def scaleset(self, value):
180dcee3bd3SJeff Roberson		self.target.scaleset(int(value))
181dcee3bd3SJeff Roberson
182dcee3bd3SJeff Roberson	def set(self, value):
183dcee3bd3SJeff Roberson		self.scale.set(value)
184dcee3bd3SJeff Roberson
18516ef0f3bSJeff Roberson	def setmax(self, value):
18616ef0f3bSJeff Roberson		#
18716ef0f3bSJeff Roberson		# We can't reconfigure the to_ value so we delete the old
18816ef0f3bSJeff Roberson		# window and make a new one when we resize.
18916ef0f3bSJeff Roberson		#
19016ef0f3bSJeff Roberson		if (self.scale != None):
19116ef0f3bSJeff Roberson			self.scale.pack_forget()
19216ef0f3bSJeff Roberson			self.scale.destroy()
19316ef0f3bSJeff Roberson		self.scale = Scale(self, command=self.scaleset,
19416ef0f3bSJeff Roberson		    from_=100, to_=value, orient=HORIZONTAL,
19516ef0f3bSJeff Roberson		    resolution=self.resolution)
19616ef0f3bSJeff Roberson		self.scale.pack(fill="both", expand=1)
19716ef0f3bSJeff Roberson		self.scale.set(self.target.scaleget())
19816ef0f3bSJeff Roberson
199dcee3bd3SJeff Robersonclass Status(Frame):
200dcee3bd3SJeff Roberson	def __init__(self, master):
201dcee3bd3SJeff Roberson		Frame.__init__(self, master)
202dcee3bd3SJeff Roberson		self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
203dcee3bd3SJeff Roberson		self.label.pack(fill="both", expand=1)
204dcee3bd3SJeff Roberson		self.clear()
205dcee3bd3SJeff Roberson
206dcee3bd3SJeff Roberson	def set(self, str):
207dcee3bd3SJeff Roberson		self.label.config(text=str)
208dcee3bd3SJeff Roberson
209dcee3bd3SJeff Roberson	def clear(self):
210dcee3bd3SJeff Roberson		self.label.config(text="")
211dcee3bd3SJeff Roberson
212dcee3bd3SJeff Roberson	def startup(self, str):
213dcee3bd3SJeff Roberson		self.set(str)
214dcee3bd3SJeff Roberson		root.update()
215dcee3bd3SJeff Roberson
216ec5cae07SJeff Robersonclass ColorConf(Frame):
217ec5cae07SJeff Roberson	def __init__(self, master, name, color):
218dcee3bd3SJeff Roberson		Frame.__init__(self, master)
219ec5cae07SJeff Roberson		if (graph.getstate(name) == "hidden"):
220ec5cae07SJeff Roberson			enabled = 0
221ec5cae07SJeff Roberson		else:
222ec5cae07SJeff Roberson			enabled = 1
223dcee3bd3SJeff Roberson		self.name = name
224dcee3bd3SJeff Roberson		self.color = StringVar()
225dcee3bd3SJeff Roberson		self.color_default = color
226dcee3bd3SJeff Roberson		self.color_current = color
227dcee3bd3SJeff Roberson		self.color.set(color)
228dcee3bd3SJeff Roberson		self.enabled = IntVar()
229dcee3bd3SJeff Roberson		self.enabled_default = enabled
230dcee3bd3SJeff Roberson		self.enabled_current = enabled
231dcee3bd3SJeff Roberson		self.enabled.set(enabled)
232dcee3bd3SJeff Roberson		self.draw()
233dcee3bd3SJeff Roberson
234dcee3bd3SJeff Roberson	def draw(self):
235dcee3bd3SJeff Roberson		self.label = Label(self, text=self.name, anchor=W)
236dcee3bd3SJeff Roberson		self.sample = Canvas(self, width=24, height=24,
237dcee3bd3SJeff Roberson		    bg='grey')
238dcee3bd3SJeff Roberson		self.rect = self.sample.create_rectangle(0, 0, 24, 24,
239dcee3bd3SJeff Roberson		    fill=self.color.get())
240ec5cae07SJeff Roberson		self.list = OptionMenu(self, self.color, command=self.setcolor,
241ec5cae07SJeff Roberson		    *colors)
242dcee3bd3SJeff Roberson		self.checkbox = Checkbutton(self, text="enabled",
243dcee3bd3SJeff Roberson		    variable=self.enabled)
244dcee3bd3SJeff Roberson		self.label.grid(row=0, column=0, sticky=E+W)
245dcee3bd3SJeff Roberson		self.sample.grid(row=0, column=1)
246dcee3bd3SJeff Roberson		self.list.grid(row=0, column=2, sticky=E+W)
247dcee3bd3SJeff Roberson		self.checkbox.grid(row=0, column=3)
248dcee3bd3SJeff Roberson		self.columnconfigure(0, weight=1)
249ec5cae07SJeff Roberson		self.columnconfigure(2, minsize=150)
250dcee3bd3SJeff Roberson
251dcee3bd3SJeff Roberson	def setcolor(self, color):
252dcee3bd3SJeff Roberson		self.color.set(color)
253dcee3bd3SJeff Roberson		self.sample.itemconfigure(self.rect, fill=color)
254dcee3bd3SJeff Roberson
255dcee3bd3SJeff Roberson	def apply(self):
256dcee3bd3SJeff Roberson		cchange = 0
257dcee3bd3SJeff Roberson		echange = 0
258dcee3bd3SJeff Roberson		if (self.color_current != self.color.get()):
259dcee3bd3SJeff Roberson			cchange = 1
260dcee3bd3SJeff Roberson		if (self.enabled_current != self.enabled.get()):
261dcee3bd3SJeff Roberson			echange = 1
262dcee3bd3SJeff Roberson		self.color_current = self.color.get()
263dcee3bd3SJeff Roberson		self.enabled_current = self.enabled.get()
264dcee3bd3SJeff Roberson		if (echange != 0):
265dcee3bd3SJeff Roberson			if (self.enabled_current):
266dcee3bd3SJeff Roberson				graph.setcolor(self.name, self.color_current)
267dcee3bd3SJeff Roberson			else:
268dcee3bd3SJeff Roberson				graph.hide(self.name)
269dcee3bd3SJeff Roberson			return
270dcee3bd3SJeff Roberson		if (cchange != 0):
271dcee3bd3SJeff Roberson			graph.setcolor(self.name, self.color_current)
272dcee3bd3SJeff Roberson
273dcee3bd3SJeff Roberson	def revert(self):
274dcee3bd3SJeff Roberson		self.setcolor(self.color_default)
275dcee3bd3SJeff Roberson		self.enabled.set(self.enabled_default)
276dcee3bd3SJeff Roberson
277ec5cae07SJeff Robersonclass ColorConfigure(Toplevel):
278ec5cae07SJeff Roberson	def __init__(self, table, name):
279dcee3bd3SJeff Roberson		Toplevel.__init__(self)
280dcee3bd3SJeff Roberson		self.resizable(0, 0)
281ec5cae07SJeff Roberson		self.title(name)
282ec5cae07SJeff Roberson		self.items = LabelFrame(self, text="Item Type")
283dcee3bd3SJeff Roberson		self.buttons = Frame(self)
284dcee3bd3SJeff Roberson		self.drawbuttons()
285dcee3bd3SJeff Roberson		self.items.grid(row=0, column=0, sticky=E+W)
286dcee3bd3SJeff Roberson		self.columnconfigure(0, weight=1)
287dcee3bd3SJeff Roberson		self.buttons.grid(row=1, column=0, sticky=E+W)
288dcee3bd3SJeff Roberson		self.types = []
289dcee3bd3SJeff Roberson		self.irow = 0
290ec5cae07SJeff Roberson		for type in table:
291ec5cae07SJeff Roberson			color = graph.getcolor(type[0])
292ec5cae07SJeff Roberson			if (color != ""):
293ec5cae07SJeff Roberson				self.additem(type[0], color)
2949799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
2959799411bSJohn Baldwin
2969799411bSJohn Baldwin	def destroycb(self, event):
2979799411bSJohn Baldwin		self.destroy()
298dcee3bd3SJeff Roberson
299ec5cae07SJeff Roberson	def additem(self, name, color):
300ec5cae07SJeff Roberson		item = ColorConf(self.items, name, color)
301dcee3bd3SJeff Roberson		self.types.append(item)
302dcee3bd3SJeff Roberson		item.grid(row=self.irow, column=0, sticky=E+W)
303dcee3bd3SJeff Roberson		self.irow += 1
304dcee3bd3SJeff Roberson
305dcee3bd3SJeff Roberson	def drawbuttons(self):
306dcee3bd3SJeff Roberson		self.apply = Button(self.buttons, text="Apply",
307dcee3bd3SJeff Roberson		    command=self.apress)
308ec5cae07SJeff Roberson		self.default = Button(self.buttons, text="Revert",
309dcee3bd3SJeff Roberson		    command=self.rpress)
310dcee3bd3SJeff Roberson		self.apply.grid(row=0, column=0, sticky=E+W)
311ec5cae07SJeff Roberson		self.default.grid(row=0, column=1, sticky=E+W)
312dcee3bd3SJeff Roberson		self.buttons.columnconfigure(0, weight=1)
313dcee3bd3SJeff Roberson		self.buttons.columnconfigure(1, weight=1)
314dcee3bd3SJeff Roberson
315dcee3bd3SJeff Roberson	def apress(self):
316dcee3bd3SJeff Roberson		for item in self.types:
317dcee3bd3SJeff Roberson			item.apply()
318dcee3bd3SJeff Roberson
319dcee3bd3SJeff Roberson	def rpress(self):
320dcee3bd3SJeff Roberson		for item in self.types:
321dcee3bd3SJeff Roberson			item.revert()
322dcee3bd3SJeff Roberson
3233d21f0f4SJeff Robersonclass SourceConf(Frame):
3243d21f0f4SJeff Roberson	def __init__(self, master, source):
3253d21f0f4SJeff Roberson		Frame.__init__(self, master)
3263d21f0f4SJeff Roberson		if (source.hidden == 1):
3273d21f0f4SJeff Roberson			enabled = 0
3283d21f0f4SJeff Roberson		else:
3293d21f0f4SJeff Roberson			enabled = 1
3303d21f0f4SJeff Roberson		self.source = source
3313d21f0f4SJeff Roberson		self.name = source.name
3323d21f0f4SJeff Roberson		self.enabled = IntVar()
3333d21f0f4SJeff Roberson		self.enabled_default = enabled
3343d21f0f4SJeff Roberson		self.enabled_current = enabled
3353d21f0f4SJeff Roberson		self.enabled.set(enabled)
3363d21f0f4SJeff Roberson		self.draw()
3373d21f0f4SJeff Roberson
3383d21f0f4SJeff Roberson	def draw(self):
3393d21f0f4SJeff Roberson		self.label = Label(self, text=self.name, anchor=W)
3403d21f0f4SJeff Roberson		self.checkbox = Checkbutton(self, text="enabled",
3413d21f0f4SJeff Roberson		    variable=self.enabled)
3423d21f0f4SJeff Roberson		self.label.grid(row=0, column=0, sticky=E+W)
3433d21f0f4SJeff Roberson		self.checkbox.grid(row=0, column=1)
3443d21f0f4SJeff Roberson		self.columnconfigure(0, weight=1)
3453d21f0f4SJeff Roberson
34650d670daSJeff Roberson	def changed(self):
3473d21f0f4SJeff Roberson		if (self.enabled_current != self.enabled.get()):
34850d670daSJeff Roberson			return 1
34950d670daSJeff Roberson		return 0
35050d670daSJeff Roberson
35150d670daSJeff Roberson	def apply(self):
3523d21f0f4SJeff Roberson		self.enabled_current = self.enabled.get()
3533d21f0f4SJeff Roberson
3543d21f0f4SJeff Roberson	def revert(self):
3553d21f0f4SJeff Roberson		self.enabled.set(self.enabled_default)
3563d21f0f4SJeff Roberson
3573d21f0f4SJeff Roberson	def check(self):
3583d21f0f4SJeff Roberson		self.enabled.set(1)
3593d21f0f4SJeff Roberson
3603d21f0f4SJeff Roberson	def uncheck(self):
3613d21f0f4SJeff Roberson		self.enabled.set(0)
3623d21f0f4SJeff Roberson
3633d21f0f4SJeff Robersonclass SourceConfigure(Toplevel):
3643d21f0f4SJeff Roberson	def __init__(self):
3653d21f0f4SJeff Roberson		Toplevel.__init__(self)
3663d21f0f4SJeff Roberson		self.resizable(0, 0)
3673d21f0f4SJeff Roberson		self.title("Source Configuration")
3683d21f0f4SJeff Roberson		self.items = []
3693d21f0f4SJeff Roberson		self.iframe = Frame(self)
3703d21f0f4SJeff Roberson		self.iframe.grid(row=0, column=0, sticky=E+W)
3713d21f0f4SJeff Roberson		f = LabelFrame(self.iframe, bd=4, text="Sources")
3723d21f0f4SJeff Roberson		self.items.append(f)
3733d21f0f4SJeff Roberson		self.buttons = Frame(self)
3743d21f0f4SJeff Roberson		self.items[0].grid(row=0, column=0, sticky=E+W)
3753d21f0f4SJeff Roberson		self.columnconfigure(0, weight=1)
3763d21f0f4SJeff Roberson		self.sconfig = []
3773d21f0f4SJeff Roberson		self.irow = 0
3783d21f0f4SJeff Roberson		self.icol = 0
3793d21f0f4SJeff Roberson		for source in sources:
3803d21f0f4SJeff Roberson			self.addsource(source)
3813d21f0f4SJeff Roberson		self.drawbuttons()
3823d21f0f4SJeff Roberson		self.buttons.grid(row=1, column=0, sticky=W)
3839799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
3849799411bSJohn Baldwin
3859799411bSJohn Baldwin	def destroycb(self, event):
3869799411bSJohn Baldwin		self.destroy()
3873d21f0f4SJeff Roberson
3883d21f0f4SJeff Roberson	def addsource(self, source):
3893d21f0f4SJeff Roberson		if (self.irow > 30):
3903d21f0f4SJeff Roberson			self.icol += 1
3913d21f0f4SJeff Roberson			self.irow = 0
3923d21f0f4SJeff Roberson			c = self.icol
3933d21f0f4SJeff Roberson			f = LabelFrame(self.iframe, bd=4, text="Sources")
3943d21f0f4SJeff Roberson			f.grid(row=0, column=c, sticky=N+E+W)
3953d21f0f4SJeff Roberson			self.items.append(f)
3963d21f0f4SJeff Roberson		item = SourceConf(self.items[self.icol], source)
3973d21f0f4SJeff Roberson		self.sconfig.append(item)
3983d21f0f4SJeff Roberson		item.grid(row=self.irow, column=0, sticky=E+W)
3993d21f0f4SJeff Roberson		self.irow += 1
4003d21f0f4SJeff Roberson
4013d21f0f4SJeff Roberson	def drawbuttons(self):
4023d21f0f4SJeff Roberson		self.apply = Button(self.buttons, text="Apply",
4033d21f0f4SJeff Roberson		    command=self.apress)
4043d21f0f4SJeff Roberson		self.default = Button(self.buttons, text="Revert",
4053d21f0f4SJeff Roberson		    command=self.rpress)
4063d21f0f4SJeff Roberson		self.checkall = Button(self.buttons, text="Check All",
4073d21f0f4SJeff Roberson		    command=self.cpress)
4083d21f0f4SJeff Roberson		self.uncheckall = Button(self.buttons, text="Uncheck All",
4093d21f0f4SJeff Roberson		    command=self.upress)
4103d21f0f4SJeff Roberson		self.checkall.grid(row=0, column=0, sticky=W)
4113d21f0f4SJeff Roberson		self.uncheckall.grid(row=0, column=1, sticky=W)
4123d21f0f4SJeff Roberson		self.apply.grid(row=0, column=2, sticky=W)
4133d21f0f4SJeff Roberson		self.default.grid(row=0, column=3, sticky=W)
4143d21f0f4SJeff Roberson		self.buttons.columnconfigure(0, weight=1)
4153d21f0f4SJeff Roberson		self.buttons.columnconfigure(1, weight=1)
4163d21f0f4SJeff Roberson		self.buttons.columnconfigure(2, weight=1)
4173d21f0f4SJeff Roberson		self.buttons.columnconfigure(3, weight=1)
4183d21f0f4SJeff Roberson
4193d21f0f4SJeff Roberson	def apress(self):
42050d670daSJeff Roberson		disable_sources = []
42150d670daSJeff Roberson		enable_sources = []
42250d670daSJeff Roberson		for item in self.sconfig:
42350d670daSJeff Roberson			if (item.changed() == 0):
42450d670daSJeff Roberson				continue
42550d670daSJeff Roberson			if (item.enabled.get() == 1):
42650d670daSJeff Roberson				enable_sources.append(item.source)
42750d670daSJeff Roberson			else:
42850d670daSJeff Roberson				disable_sources.append(item.source)
42950d670daSJeff Roberson
43050d670daSJeff Roberson		if (len(disable_sources)):
43150d670daSJeff Roberson			graph.sourcehidelist(disable_sources)
43250d670daSJeff Roberson		if (len(enable_sources)):
43350d670daSJeff Roberson			graph.sourceshowlist(enable_sources)
43450d670daSJeff Roberson
4353d21f0f4SJeff Roberson		for item in self.sconfig:
4363d21f0f4SJeff Roberson			item.apply()
4373d21f0f4SJeff Roberson
4383d21f0f4SJeff Roberson	def rpress(self):
4393d21f0f4SJeff Roberson		for item in self.sconfig:
4403d21f0f4SJeff Roberson			item.revert()
4413d21f0f4SJeff Roberson
4423d21f0f4SJeff Roberson	def cpress(self):
4433d21f0f4SJeff Roberson		for item in self.sconfig:
4443d21f0f4SJeff Roberson			item.check()
4453d21f0f4SJeff Roberson
4463d21f0f4SJeff Roberson	def upress(self):
4473d21f0f4SJeff Roberson		for item in self.sconfig:
4483d21f0f4SJeff Roberson			item.uncheck()
4493d21f0f4SJeff Roberson
45050d670daSJeff Robersonclass SourceStats(Toplevel):
45150d670daSJeff Roberson	def __init__(self, source):
45250d670daSJeff Roberson		self.source = source
45350d670daSJeff Roberson		Toplevel.__init__(self)
45450d670daSJeff Roberson		self.resizable(0, 0)
45550d670daSJeff Roberson		self.title(source.name + " statistics")
45650d670daSJeff Roberson		self.evframe = LabelFrame(self,
457b62baf95SJeff Roberson		    text="Event Count, Duration, Avg Duration")
45850d670daSJeff Roberson		self.evframe.grid(row=0, column=0, sticky=E+W)
45950d670daSJeff Roberson		eventtypes={}
46050d670daSJeff Roberson		for event in self.source.events:
46150d670daSJeff Roberson			if (event.type == "pad"):
46250d670daSJeff Roberson				continue
46350d670daSJeff Roberson			duration = event.duration
464aef675d8SCraig Rodrigues			if (event.name in eventtypes):
46550d670daSJeff Roberson				(c, d) = eventtypes[event.name]
46650d670daSJeff Roberson				c += 1
46750d670daSJeff Roberson				d += duration
46850d670daSJeff Roberson				eventtypes[event.name] = (c, d)
46950d670daSJeff Roberson			else:
47050d670daSJeff Roberson				eventtypes[event.name] = (1, duration)
47150d670daSJeff Roberson		events = []
47250d670daSJeff Roberson		for k, v in eventtypes.iteritems():
47350d670daSJeff Roberson			(c, d) = v
47450d670daSJeff Roberson			events.append((k, c, d))
475*7e8ed296SAndriy Gapon		events.sort(key=itemgetter(1), reverse=True)
47650d670daSJeff Roberson
47750d670daSJeff Roberson		ypos = 0
47850d670daSJeff Roberson		for event in events:
47950d670daSJeff Roberson			(name, c, d) = event
480b62baf95SJeff Roberson			Label(self.evframe, text=name, bd=1,
481b62baf95SJeff Roberson			    relief=SUNKEN, anchor=W, width=30).grid(
482b62baf95SJeff Roberson			    row=ypos, column=0, sticky=W+E)
483b62baf95SJeff Roberson			Label(self.evframe, text=str(c), bd=1,
484b62baf95SJeff Roberson			    relief=SUNKEN, anchor=W, width=10).grid(
485b62baf95SJeff Roberson			    row=ypos, column=1, sticky=W+E)
486b62baf95SJeff Roberson			Label(self.evframe, text=ticks2sec(d),
487b62baf95SJeff Roberson			    bd=1, relief=SUNKEN, width=10).grid(
488b62baf95SJeff Roberson			    row=ypos, column=2, sticky=W+E)
489b62baf95SJeff Roberson			if (d and c):
490b62baf95SJeff Roberson				d /= c
491b62baf95SJeff Roberson			else:
492b62baf95SJeff Roberson				d = 0
493b62baf95SJeff Roberson			Label(self.evframe, text=ticks2sec(d),
494b62baf95SJeff Roberson			    bd=1, relief=SUNKEN, width=10).grid(
495b62baf95SJeff Roberson			    row=ypos, column=3, sticky=W+E)
49650d670daSJeff Roberson			ypos += 1
4979799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
4989799411bSJohn Baldwin
4999799411bSJohn Baldwin	def destroycb(self, event):
5009799411bSJohn Baldwin		self.destroy()
50150d670daSJeff Roberson
50250d670daSJeff Roberson
50350d670daSJeff Robersonclass SourceContext(Menu):
50450d670daSJeff Roberson	def __init__(self, event, source):
50550d670daSJeff Roberson		self.source = source
50650d670daSJeff Roberson		Menu.__init__(self, tearoff=0, takefocus=0)
50750d670daSJeff Roberson		self.add_command(label="hide", command=self.hide)
50850d670daSJeff Roberson		self.add_command(label="hide group", command=self.hidegroup)
50950d670daSJeff Roberson		self.add_command(label="stats", command=self.stats)
51050d670daSJeff Roberson		self.tk_popup(event.x_root-3, event.y_root+3)
51150d670daSJeff Roberson
51250d670daSJeff Roberson	def hide(self):
51350d670daSJeff Roberson		graph.sourcehide(self.source)
51450d670daSJeff Roberson
51550d670daSJeff Roberson	def hidegroup(self):
51650d670daSJeff Roberson		grouplist = []
51750d670daSJeff Roberson		for source in sources:
51850d670daSJeff Roberson			if (source.group == self.source.group):
51950d670daSJeff Roberson				grouplist.append(source)
52050d670daSJeff Roberson		graph.sourcehidelist(grouplist)
52150d670daSJeff Roberson
52250d670daSJeff Roberson	def show(self):
52350d670daSJeff Roberson		graph.sourceshow(self.source)
52450d670daSJeff Roberson
52550d670daSJeff Roberson	def stats(self):
52650d670daSJeff Roberson		SourceStats(self.source)
52750d670daSJeff Roberson
528dcee3bd3SJeff Robersonclass EventView(Toplevel):
529dcee3bd3SJeff Roberson	def __init__(self, event, canvas):
530dcee3bd3SJeff Roberson		Toplevel.__init__(self)
531dcee3bd3SJeff Roberson		self.resizable(0, 0)
532dcee3bd3SJeff Roberson		self.title("Event")
533dcee3bd3SJeff Roberson		self.event = event
534dcee3bd3SJeff Roberson		self.buttons = Frame(self)
535ec5cae07SJeff Roberson		self.buttons.grid(row=0, column=0, sticky=E+W)
536ec5cae07SJeff Roberson		self.frame = Frame(self)
537ec5cae07SJeff Roberson		self.frame.grid(row=1, column=0, sticky=N+S+E+W)
538dcee3bd3SJeff Roberson		self.canvas = canvas
539dcee3bd3SJeff Roberson		self.drawlabels()
540dcee3bd3SJeff Roberson		self.drawbuttons()
541dcee3bd3SJeff Roberson		event.displayref(canvas)
542dcee3bd3SJeff Roberson		self.bind("<Destroy>", self.destroycb)
5439799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
544dcee3bd3SJeff Roberson
545dcee3bd3SJeff Roberson	def destroycb(self, event):
546dcee3bd3SJeff Roberson		self.unbind("<Destroy>")
547dcee3bd3SJeff Roberson		if (self.event != None):
548dcee3bd3SJeff Roberson			self.event.displayunref(self.canvas)
549dcee3bd3SJeff Roberson			self.event = None
550dcee3bd3SJeff Roberson		self.destroy()
551dcee3bd3SJeff Roberson
552dcee3bd3SJeff Roberson	def clearlabels(self):
553dcee3bd3SJeff Roberson		for label in self.frame.grid_slaves():
554dcee3bd3SJeff Roberson			label.grid_remove()
555dcee3bd3SJeff Roberson
556dcee3bd3SJeff Roberson	def drawlabels(self):
557dcee3bd3SJeff Roberson		ypos = 0
558dcee3bd3SJeff Roberson		labels = self.event.labels()
559dcee3bd3SJeff Roberson		while (len(labels) < 7):
560ec5cae07SJeff Roberson			labels.append(("", ""))
561dcee3bd3SJeff Roberson		for label in labels:
562ec5cae07SJeff Roberson			name, value = label
563ec5cae07SJeff Roberson			linked = 0
564ec5cae07SJeff Roberson			if (name == "linkedto"):
565ec5cae07SJeff Roberson				linked = 1
566dcee3bd3SJeff Roberson			l = Label(self.frame, text=name, bd=1, width=15,
567dcee3bd3SJeff Roberson			    relief=SUNKEN, anchor=W)
568dcee3bd3SJeff Roberson			if (linked):
569dcee3bd3SJeff Roberson				fgcolor = "blue"
570dcee3bd3SJeff Roberson			else:
571dcee3bd3SJeff Roberson				fgcolor = "black"
572dcee3bd3SJeff Roberson			r = Label(self.frame, text=value, bd=1,
573dcee3bd3SJeff Roberson			    relief=SUNKEN, anchor=W, fg=fgcolor)
574dcee3bd3SJeff Roberson			l.grid(row=ypos, column=0, sticky=E+W)
575dcee3bd3SJeff Roberson			r.grid(row=ypos, column=1, sticky=E+W)
576dcee3bd3SJeff Roberson			if (linked):
577dcee3bd3SJeff Roberson				r.bind("<Button-1>", self.linkpress)
578dcee3bd3SJeff Roberson			ypos += 1
579dcee3bd3SJeff Roberson		self.frame.columnconfigure(1, minsize=80)
580dcee3bd3SJeff Roberson
581dcee3bd3SJeff Roberson	def drawbuttons(self):
582dcee3bd3SJeff Roberson		self.back = Button(self.buttons, text="<", command=self.bpress)
583dcee3bd3SJeff Roberson		self.forw = Button(self.buttons, text=">", command=self.fpress)
584dcee3bd3SJeff Roberson		self.new = Button(self.buttons, text="new", command=self.npress)
585dcee3bd3SJeff Roberson		self.back.grid(row=0, column=0, sticky=E+W)
586dcee3bd3SJeff Roberson		self.forw.grid(row=0, column=1, sticky=E+W)
587dcee3bd3SJeff Roberson		self.new.grid(row=0, column=2, sticky=E+W)
588dcee3bd3SJeff Roberson		self.buttons.columnconfigure(2, weight=1)
589dcee3bd3SJeff Roberson
590dcee3bd3SJeff Roberson	def newevent(self, event):
591dcee3bd3SJeff Roberson		self.event.displayunref(self.canvas)
592dcee3bd3SJeff Roberson		self.clearlabels()
593dcee3bd3SJeff Roberson		self.event = event
594dcee3bd3SJeff Roberson		self.event.displayref(self.canvas)
595dcee3bd3SJeff Roberson		self.drawlabels()
596dcee3bd3SJeff Roberson
597dcee3bd3SJeff Roberson	def npress(self):
598dcee3bd3SJeff Roberson		EventView(self.event, self.canvas)
599dcee3bd3SJeff Roberson
600dcee3bd3SJeff Roberson	def bpress(self):
601dcee3bd3SJeff Roberson		prev = self.event.prev()
602dcee3bd3SJeff Roberson		if (prev == None):
603dcee3bd3SJeff Roberson			return
604ec5cae07SJeff Roberson		while (prev.type == "pad"):
605dcee3bd3SJeff Roberson			prev = prev.prev()
606dcee3bd3SJeff Roberson			if (prev == None):
607dcee3bd3SJeff Roberson				return
608dcee3bd3SJeff Roberson		self.newevent(prev)
609dcee3bd3SJeff Roberson
610dcee3bd3SJeff Roberson	def fpress(self):
611dcee3bd3SJeff Roberson		next = self.event.next()
612dcee3bd3SJeff Roberson		if (next == None):
613dcee3bd3SJeff Roberson			return
614ec5cae07SJeff Roberson		while (next.type == "pad"):
615dcee3bd3SJeff Roberson			next = next.next()
616dcee3bd3SJeff Roberson			if (next == None):
617dcee3bd3SJeff Roberson				return
618dcee3bd3SJeff Roberson		self.newevent(next)
619dcee3bd3SJeff Roberson
620dcee3bd3SJeff Roberson	def linkpress(self, wevent):
621dcee3bd3SJeff Roberson		event = self.event.getlinked()
622dcee3bd3SJeff Roberson		if (event != None):
623dcee3bd3SJeff Roberson			self.newevent(event)
624dcee3bd3SJeff Roberson
625dcee3bd3SJeff Robersonclass Event:
626ec5cae07SJeff Roberson	def __init__(self, source, name, cpu, timestamp, attrs):
627dcee3bd3SJeff Roberson		self.source = source
628ec5cae07SJeff Roberson		self.name = name
629dcee3bd3SJeff Roberson		self.cpu = cpu
630dcee3bd3SJeff Roberson		self.timestamp = int(timestamp)
631ec5cae07SJeff Roberson		self.attrs = attrs
632dcee3bd3SJeff Roberson		self.idx = None
633dcee3bd3SJeff Roberson		self.item = None
634dcee3bd3SJeff Roberson		self.dispcnt = 0
63550d670daSJeff Roberson		self.duration = 0
63666835de4SSam Leffler		self.recno = lineno
637dcee3bd3SJeff Roberson
638dcee3bd3SJeff Roberson	def status(self):
639dcee3bd3SJeff Roberson		statstr = self.name + " " + self.source.name
640dcee3bd3SJeff Roberson		statstr += " on: cpu" + str(self.cpu)
641dcee3bd3SJeff Roberson		statstr += " at: " + str(self.timestamp)
642ec5cae07SJeff Roberson		statstr += " attributes: "
643ec5cae07SJeff Roberson		for i in range(0, len(self.attrs)):
644ec5cae07SJeff Roberson			attr = self.attrs[i]
645ec5cae07SJeff Roberson			statstr += attr[0] + ": " + str(attr[1])
646ec5cae07SJeff Roberson			if (i != len(self.attrs) - 1):
647ec5cae07SJeff Roberson				statstr += ", "
648dcee3bd3SJeff Roberson		status.set(statstr)
649dcee3bd3SJeff Roberson
650dcee3bd3SJeff Roberson	def labels(self):
651ec5cae07SJeff Roberson		return [("Source", self.source.name),
652ec5cae07SJeff Roberson			("Event", self.name),
653ec5cae07SJeff Roberson			("CPU", self.cpu),
654ec5cae07SJeff Roberson			("Timestamp", self.timestamp),
655ec5cae07SJeff Roberson			("KTR Line ", self.recno)
656ec5cae07SJeff Roberson		] + self.attrs
657ec5cae07SJeff Roberson
658ec5cae07SJeff Roberson	def mouseenter(self, canvas):
659dcee3bd3SJeff Roberson		self.displayref(canvas)
660dcee3bd3SJeff Roberson		self.status()
661dcee3bd3SJeff Roberson
662ec5cae07SJeff Roberson	def mouseexit(self, canvas):
663dcee3bd3SJeff Roberson		self.displayunref(canvas)
664dcee3bd3SJeff Roberson		status.clear()
665dcee3bd3SJeff Roberson
666ec5cae07SJeff Roberson	def mousepress(self, canvas):
667dcee3bd3SJeff Roberson		EventView(self, canvas)
668dcee3bd3SJeff Roberson
669ec5cae07SJeff Roberson	def draw(self, canvas, xpos, ypos, item):
670ec5cae07SJeff Roberson		self.item = item
671ec5cae07SJeff Roberson		if (item != None):
672ec5cae07SJeff Roberson			canvas.items[item] = self
673ec5cae07SJeff Roberson
674ec5cae07SJeff Roberson	def move(self, canvas, x, y):
675ec5cae07SJeff Roberson		if (self.item == None):
676ec5cae07SJeff Roberson			return;
677ec5cae07SJeff Roberson		canvas.move(self.item, x, y);
678ec5cae07SJeff Roberson
679dcee3bd3SJeff Roberson	def next(self):
680dcee3bd3SJeff Roberson		return self.source.eventat(self.idx + 1)
681dcee3bd3SJeff Roberson
682ec5cae07SJeff Roberson	def nexttype(self, type):
683ec5cae07SJeff Roberson		next = self.next()
684ec5cae07SJeff Roberson		while (next != None and next.type != type):
685ec5cae07SJeff Roberson			next = next.next()
686ec5cae07SJeff Roberson		return (next)
687ec5cae07SJeff Roberson
688dcee3bd3SJeff Roberson	def prev(self):
689dcee3bd3SJeff Roberson		return self.source.eventat(self.idx - 1)
690dcee3bd3SJeff Roberson
691dcee3bd3SJeff Roberson	def displayref(self, canvas):
692dcee3bd3SJeff Roberson		if (self.dispcnt == 0):
693dcee3bd3SJeff Roberson			canvas.itemconfigure(self.item, width=2)
694dcee3bd3SJeff Roberson		self.dispcnt += 1
695dcee3bd3SJeff Roberson
696dcee3bd3SJeff Roberson	def displayunref(self, canvas):
697dcee3bd3SJeff Roberson		self.dispcnt -= 1
698dcee3bd3SJeff Roberson		if (self.dispcnt == 0):
699dcee3bd3SJeff Roberson			canvas.itemconfigure(self.item, width=0)
700dcee3bd3SJeff Roberson			canvas.tag_raise("point", "state")
701dcee3bd3SJeff Roberson
702dcee3bd3SJeff Roberson	def getlinked(self):
703ec5cae07SJeff Roberson		for attr in self.attrs:
704ec5cae07SJeff Roberson			if (attr[0] != "linkedto"):
705ec5cae07SJeff Roberson				continue
706ec5cae07SJeff Roberson			source = ktrfile.findid(attr[1])
707ec5cae07SJeff Roberson			return source.findevent(self.timestamp)
708ec5cae07SJeff Roberson		return None
709dcee3bd3SJeff Roberson
710dcee3bd3SJeff Robersonclass PointEvent(Event):
711ec5cae07SJeff Roberson	type = "point"
712ec5cae07SJeff Roberson	def __init__(self, source, name, cpu, timestamp, attrs):
713ec5cae07SJeff Roberson		Event.__init__(self, source, name, cpu, timestamp, attrs)
714dcee3bd3SJeff Roberson
715dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
716ec5cae07SJeff Roberson		color = colormap.lookup(self.name)
717932f0fa2SJeff Roberson		l = canvas.create_oval(xpos - XY_POINT, ypos,
718932f0fa2SJeff Roberson		    xpos + XY_POINT, ypos - (XY_POINT * 2),
71950d670daSJeff Roberson		    fill=color, width=0,
720932f0fa2SJeff Roberson		    tags=("event", self.type, self.name, self.source.tag))
721ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, l)
722dcee3bd3SJeff Roberson
723ec5cae07SJeff Roberson		return xpos
724dcee3bd3SJeff Roberson
725dcee3bd3SJeff Robersonclass StateEvent(Event):
726ec5cae07SJeff Roberson	type = "state"
727ec5cae07SJeff Roberson	def __init__(self, source, name, cpu, timestamp, attrs):
728ec5cae07SJeff Roberson		Event.__init__(self, source, name, cpu, timestamp, attrs)
729dcee3bd3SJeff Roberson
730dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
731ec5cae07SJeff Roberson		next = self.nexttype("state")
732dcee3bd3SJeff Roberson		if (next == None):
733ec5cae07SJeff Roberson			return (xpos)
73450d670daSJeff Roberson		self.duration = duration = next.timestamp - self.timestamp
735ec5cae07SJeff Roberson		self.attrs.insert(0, ("duration", ticks2sec(duration)))
736ec5cae07SJeff Roberson		color = colormap.lookup(self.name)
737ec5cae07SJeff Roberson		if (duration < 0):
738ec5cae07SJeff Roberson			duration = 0
739749f65e3SCraig Rodrigues			print("Unsynchronized timestamp")
740749f65e3SCraig Rodrigues			print(self.cpu, self.timestamp)
741749f65e3SCraig Rodrigues			print(next.cpu, next.timestamp)
742ec5cae07SJeff Roberson		delta = duration / canvas.ratio
743dcee3bd3SJeff Roberson		l = canvas.create_rectangle(xpos, ypos,
744ec5cae07SJeff Roberson		    xpos + delta, ypos - 10, fill=color, width=0,
745932f0fa2SJeff Roberson		    tags=("event", self.type, self.name, self.source.tag))
746ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, l)
747dcee3bd3SJeff Roberson
748dcee3bd3SJeff Roberson		return (xpos + delta)
749dcee3bd3SJeff Roberson
750ec5cae07SJeff Robersonclass CountEvent(Event):
751ec5cae07SJeff Roberson	type = "count"
752ec5cae07SJeff Roberson	def __init__(self, source, count, cpu, timestamp, attrs):
753ec5cae07SJeff Roberson		count = int(count)
754ec5cae07SJeff Roberson		self.count = count
755ec5cae07SJeff Roberson		Event.__init__(self, source, "count", cpu, timestamp, attrs)
756dcee3bd3SJeff Roberson
757dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
758ec5cae07SJeff Roberson		next = self.nexttype("count")
759ec5cae07SJeff Roberson		if (next == None):
760ec5cae07SJeff Roberson			return (xpos)
761ec5cae07SJeff Roberson		color = colormap.lookup("count")
76250d670daSJeff Roberson		self.duration = duration = next.timestamp - self.timestamp
76316ef0f3bSJeff Roberson		if (duration < 0):
76416ef0f3bSJeff Roberson			duration = 0
765749f65e3SCraig Rodrigues			print("Unsynchronized timestamp")
766749f65e3SCraig Rodrigues			print(self.cpu, self.timestamp)
767749f65e3SCraig Rodrigues			print(next.cpu, next.timestamp)
768ec5cae07SJeff Roberson		self.attrs.insert(0, ("count", self.count))
769ec5cae07SJeff Roberson		self.attrs.insert(1, ("duration", ticks2sec(duration)))
770ec5cae07SJeff Roberson		delta = duration / canvas.ratio
771dcee3bd3SJeff Roberson		yhight = self.source.yscale() * self.count
772dcee3bd3SJeff Roberson		l = canvas.create_rectangle(xpos, ypos - yhight,
773ec5cae07SJeff Roberson		    xpos + delta, ypos, fill=color, width=0,
774932f0fa2SJeff Roberson		    tags=("event", self.type, self.name, self.source.tag))
775ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, l)
776dcee3bd3SJeff Roberson		return (xpos + delta)
777dcee3bd3SJeff Roberson
778ec5cae07SJeff Robersonclass PadEvent(StateEvent):
779ec5cae07SJeff Roberson	type = "pad"
780ec5cae07SJeff Roberson	def __init__(self, source, cpu, timestamp, last=0):
781ec5cae07SJeff Roberson		if (last):
782ec5cae07SJeff Roberson			cpu = source.events[len(source.events) -1].cpu
783ec5cae07SJeff Roberson		else:
784ec5cae07SJeff Roberson			cpu = source.events[0].cpu
785ec5cae07SJeff Roberson		StateEvent.__init__(self, source, "pad", cpu, timestamp, [])
786dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
787dcee3bd3SJeff Roberson		next = self.next()
788dcee3bd3SJeff Roberson		if (next == None):
789dcee3bd3SJeff Roberson			return (xpos)
790ec5cae07SJeff Roberson		duration = next.timestamp - self.timestamp
791ec5cae07SJeff Roberson		delta = duration / canvas.ratio
792ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, None)
793dcee3bd3SJeff Roberson		return (xpos + delta)
794dcee3bd3SJeff Roberson
79550d670daSJeff Roberson
796*7e8ed296SAndriy Gapon@total_ordering
797dcee3bd3SJeff Robersonclass EventSource:
798ec5cae07SJeff Roberson	def __init__(self, group, id):
799ec5cae07SJeff Roberson		self.name = id
800dcee3bd3SJeff Roberson		self.events = []
801ec5cae07SJeff Roberson		self.cpuitems = []
8022977b8f9SJohn Baldwin		self.group = group
803ec5cae07SJeff Roberson		self.y = 0
804ec5cae07SJeff Roberson		self.item = None
8053d21f0f4SJeff Roberson		self.hidden = 0
80650d670daSJeff Roberson		self.tag = group + id
807dcee3bd3SJeff Roberson
808*7e8ed296SAndriy Gapon	def __lt__(self, other):
809*7e8ed296SAndriy Gapon		if other is None:
810*7e8ed296SAndriy Gapon			return False
811*7e8ed296SAndriy Gapon		return (self.group < other.group or
812*7e8ed296SAndriy Gapon				self.group == other.group and self.name < other.name)
813*7e8ed296SAndriy Gapon
814*7e8ed296SAndriy Gapon	def __eq__(self, other):
815*7e8ed296SAndriy Gapon		if other is None:
816*7e8ed296SAndriy Gapon			return False
817*7e8ed296SAndriy Gapon		return self.group == other.group and self.name == other.name
8182977b8f9SJohn Baldwin
8192977b8f9SJohn Baldwin	# It is much faster to append items to a list then to insert them
8202977b8f9SJohn Baldwin	# at the beginning.  As a result, we add events in reverse order
8212977b8f9SJohn Baldwin	# and then swap the list during fixup.
822dcee3bd3SJeff Roberson	def fixup(self):
8232977b8f9SJohn Baldwin		self.events.reverse()
824dcee3bd3SJeff Roberson
825ec5cae07SJeff Roberson	def addevent(self, event):
8262977b8f9SJohn Baldwin		self.events.append(event)
827dcee3bd3SJeff Roberson
828ec5cae07SJeff Roberson	def addlastevent(self, event):
8292977b8f9SJohn Baldwin		self.events.insert(0, event)
830dcee3bd3SJeff Roberson
831dcee3bd3SJeff Roberson	def draw(self, canvas, ypos):
832dcee3bd3SJeff Roberson		xpos = 10
833ec5cae07SJeff Roberson		cpux = 10
834ec5cae07SJeff Roberson		cpu = self.events[1].cpu
835dcee3bd3SJeff Roberson		for i in range(0, len(self.events)):
836dcee3bd3SJeff Roberson			self.events[i].idx = i
837dcee3bd3SJeff Roberson		for event in self.events:
838ec5cae07SJeff Roberson			if (event.cpu != cpu and event.cpu != -1):
839ec5cae07SJeff Roberson				self.drawcpu(canvas, cpu, cpux, xpos, ypos)
840ec5cae07SJeff Roberson				cpux = xpos
841ec5cae07SJeff Roberson				cpu = event.cpu
842dcee3bd3SJeff Roberson			xpos = event.draw(canvas, xpos, ypos)
843ec5cae07SJeff Roberson		self.drawcpu(canvas, cpu, cpux, xpos, ypos)
844dcee3bd3SJeff Roberson
845dcee3bd3SJeff Roberson	def drawname(self, canvas, ypos):
846ec5cae07SJeff Roberson		self.y = ypos
847dcee3bd3SJeff Roberson		ypos = ypos - (self.ysize() / 2)
848932f0fa2SJeff Roberson		self.item = canvas.create_text(X_BORDER, ypos, anchor="w",
849932f0fa2SJeff Roberson		    text=self.name)
850ec5cae07SJeff Roberson		return (self.item)
851dcee3bd3SJeff Roberson
852ec5cae07SJeff Roberson	def drawcpu(self, canvas, cpu, fromx, tox, ypos):
853ec5cae07SJeff Roberson		cpu = "CPU " + str(cpu)
854ec5cae07SJeff Roberson		color = cpucolormap.lookup(cpu)
855ec5cae07SJeff Roberson		# Create the cpu background colors default to hidden
856ec5cae07SJeff Roberson		l = canvas.create_rectangle(fromx,
857dcee3bd3SJeff Roberson		    ypos - self.ysize() - canvas.bdheight,
858ec5cae07SJeff Roberson		    tox, ypos + canvas.bdheight, fill=color, width=0,
859932f0fa2SJeff Roberson		    tags=("cpubg", cpu, self.tag), state="hidden")
860ec5cae07SJeff Roberson		self.cpuitems.append(l)
861ec5cae07SJeff Roberson
862ec5cae07SJeff Roberson	def move(self, canvas, xpos, ypos):
86350d670daSJeff Roberson		canvas.move(self.tag, xpos, ypos)
864ec5cae07SJeff Roberson
865ec5cae07SJeff Roberson	def movename(self, canvas, xpos, ypos):
866ec5cae07SJeff Roberson		self.y += ypos
867ec5cae07SJeff Roberson		canvas.move(self.item, xpos, ypos)
868dcee3bd3SJeff Roberson
869dcee3bd3SJeff Roberson	def ysize(self):
870932f0fa2SJeff Roberson		return (Y_EVENTSOURCE)
871dcee3bd3SJeff Roberson
872dcee3bd3SJeff Roberson	def eventat(self, i):
873c3db6aa6SJohn Baldwin		if (i >= len(self.events) or i < 0):
874dcee3bd3SJeff Roberson			return (None)
875dcee3bd3SJeff Roberson		event = self.events[i]
876dcee3bd3SJeff Roberson		return (event)
877dcee3bd3SJeff Roberson
878dcee3bd3SJeff Roberson	def findevent(self, timestamp):
879dcee3bd3SJeff Roberson		for event in self.events:
880ec5cae07SJeff Roberson			if (event.timestamp >= timestamp and event.type != "pad"):
881dcee3bd3SJeff Roberson				return (event)
882dcee3bd3SJeff Roberson		return (None)
883dcee3bd3SJeff Roberson
884dcee3bd3SJeff Robersonclass Counter(EventSource):
885ec5cae07SJeff Roberson	#
886ec5cae07SJeff Roberson	# Store a hash of counter groups that keeps the max value
887ec5cae07SJeff Roberson	# for a counter in this group for scaling purposes.
888ec5cae07SJeff Roberson	#
889ec5cae07SJeff Roberson	groups = {}
890ec5cae07SJeff Roberson	def __init__(self, group, id):
891dcee3bd3SJeff Roberson		try:
892ec5cae07SJeff Roberson			Counter.cnt = Counter.groups[group]
893dcee3bd3SJeff Roberson		except:
894ec5cae07SJeff Roberson			Counter.groups[group] = 0
895ec5cae07SJeff Roberson		EventSource.__init__(self, group, id)
896ec5cae07SJeff Roberson
897ec5cae07SJeff Roberson	def fixup(self):
898ec5cae07SJeff Roberson		for event in self.events:
899ec5cae07SJeff Roberson			if (event.type != "count"):
900ec5cae07SJeff Roberson				continue;
901ec5cae07SJeff Roberson			count = int(event.count)
902ec5cae07SJeff Roberson			if (count > Counter.groups[self.group]):
903ec5cae07SJeff Roberson				Counter.groups[self.group] = count
904ec5cae07SJeff Roberson		EventSource.fixup(self)
905dcee3bd3SJeff Roberson
90666835de4SSam Leffler	def ymax(self):
907ec5cae07SJeff Roberson		return (Counter.groups[self.group])
90866835de4SSam Leffler
909dcee3bd3SJeff Roberson	def ysize(self):
910932f0fa2SJeff Roberson		return (Y_COUNTER)
911dcee3bd3SJeff Roberson
912dcee3bd3SJeff Roberson	def yscale(self):
913ec5cae07SJeff Roberson		return (self.ysize() / self.ymax())
914dcee3bd3SJeff Roberson
915dcee3bd3SJeff Robersonclass KTRFile:
916dcee3bd3SJeff Roberson	def __init__(self, file):
9170482a607SJeff Roberson		self.timestamp_f = None
9180482a607SJeff Roberson		self.timestamp_l = None
9190199a61dSJohn Baldwin		self.locks = {}
920dcee3bd3SJeff Roberson		self.ticks = {}
921dcee3bd3SJeff Roberson		self.load = {}
92201e7fb47SScott Long		self.crit = {}
9230482a607SJeff Roberson		self.stathz = 0
924932f0fa2SJeff Roberson		self.eventcnt = 0
92516ef0f3bSJeff Roberson		self.taghash = {}
926dcee3bd3SJeff Roberson
927dcee3bd3SJeff Roberson		self.parse(file)
928dcee3bd3SJeff Roberson		self.fixup()
929dcee3bd3SJeff Roberson		global ticksps
9300482a607SJeff Roberson		ticksps = self.ticksps()
931932f0fa2SJeff Roberson		span = self.timespan()
932932f0fa2SJeff Roberson		ghz = float(ticksps) / 1000000000.0
933932f0fa2SJeff Roberson		#
934932f0fa2SJeff Roberson		# Update the title with some stats from the file
935932f0fa2SJeff Roberson		#
936932f0fa2SJeff Roberson		titlestr = "SchedGraph: "
937932f0fa2SJeff Roberson		titlestr += ticks2sec(span) + " at %.3f ghz, " % ghz
938932f0fa2SJeff Roberson		titlestr += str(len(sources)) + " event sources, "
939932f0fa2SJeff Roberson		titlestr += str(self.eventcnt) + " events"
940932f0fa2SJeff Roberson		root.title(titlestr)
941dcee3bd3SJeff Roberson
942dcee3bd3SJeff Roberson	def parse(self, file):
943dcee3bd3SJeff Roberson		try:
944dcee3bd3SJeff Roberson			ifp = open(file)
945dcee3bd3SJeff Roberson		except:
946749f65e3SCraig Rodrigues			print("Can't open", file)
947dcee3bd3SJeff Roberson			sys.exit(1)
948dcee3bd3SJeff Roberson
949ec5cae07SJeff Roberson		# quoteexp matches a quoted string, no escaping
950ec5cae07SJeff Roberson		quoteexp = "\"([^\"]*)\""
951dcee3bd3SJeff Roberson
952ec5cae07SJeff Roberson		#
953ec5cae07SJeff Roberson		# commaexp matches a quoted string OR the string up
954ec5cae07SJeff Roberson		# to the first ','
955ec5cae07SJeff Roberson		#
956ec5cae07SJeff Roberson		commaexp = "(?:" + quoteexp + "|([^,]+))"
957dcee3bd3SJeff Roberson
958ec5cae07SJeff Roberson		#
959ec5cae07SJeff Roberson		# colonstr matches a quoted string OR the string up
960ec5cae07SJeff Roberson		# to the first ':'
961ec5cae07SJeff Roberson		#
962ec5cae07SJeff Roberson		colonexp = "(?:" + quoteexp + "|([^:]+))"
963dcee3bd3SJeff Roberson
964ec5cae07SJeff Roberson		#
965ec5cae07SJeff Roberson		# Match various manditory parts of the KTR string this is
966ec5cae07SJeff Roberson		# fairly inflexible until you get to attributes to make
967ec5cae07SJeff Roberson		# parsing faster.
968ec5cae07SJeff Roberson		#
969ec5cae07SJeff Roberson		hdrexp = "\s*(\d+)\s+(\d+)\s+(\d+)\s+"
970ec5cae07SJeff Roberson		groupexp = "KTRGRAPH group:" + quoteexp + ", "
971ec5cae07SJeff Roberson		idexp = "id:" + quoteexp + ", "
972ec5cae07SJeff Roberson		typeexp = "([^:]+):" + commaexp + ", "
973ec5cae07SJeff Roberson		attribexp = "attributes: (.*)"
974dcee3bd3SJeff Roberson
975ec5cae07SJeff Roberson		#
976ec5cae07SJeff Roberson		# Matches optional attributes in the KTR string.  This
977ec5cae07SJeff Roberson		# tolerates more variance as the users supply these values.
978ec5cae07SJeff Roberson		#
979ec5cae07SJeff Roberson		attrexp = colonexp + "\s*:\s*(?:" + commaexp + ", (.*)|"
980ec5cae07SJeff Roberson		attrexp += quoteexp +"|(.*))"
981dcee3bd3SJeff Roberson
982ec5cae07SJeff Roberson		# Precompile regexp
983ec5cae07SJeff Roberson		ktrre = re.compile(hdrexp + groupexp + idexp + typeexp + attribexp)
984ec5cae07SJeff Roberson		attrre = re.compile(attrexp)
985dcee3bd3SJeff Roberson
98666835de4SSam Leffler		global lineno
98766835de4SSam Leffler		lineno = 0
988dbad07bfSJeff Roberson		for line in ifp.readlines():
98966835de4SSam Leffler			lineno += 1
990ec5cae07SJeff Roberson			if ((lineno % 2048) == 0):
99166835de4SSam Leffler				status.startup("Parsing line " + str(lineno))
992ec5cae07SJeff Roberson			m = ktrre.match(line);
9932e2e6cc9SJeff Roberson			if (m == None):
994749f65e3SCraig Rodrigues				print("Can't parse", lineno, line, end=' ')
995ec5cae07SJeff Roberson				continue;
996ec5cae07SJeff Roberson			(index, cpu, timestamp, group, id, type, dat, dat1, attrstring) = m.groups();
997ec5cae07SJeff Roberson			if (dat == None):
998ec5cae07SJeff Roberson				dat = dat1
999ec5cae07SJeff Roberson			if (self.checkstamp(timestamp) == 0):
1000749f65e3SCraig Rodrigues				print("Bad timestamp at", lineno, ":", end=' ')
1001749f65e3SCraig Rodrigues				print(cpu, timestamp)
1002ec5cae07SJeff Roberson				continue
1003ec5cae07SJeff Roberson			#
1004ec5cae07SJeff Roberson			# Build the table of optional attributes
1005ec5cae07SJeff Roberson			#
1006ec5cae07SJeff Roberson			attrs = []
1007ec5cae07SJeff Roberson			while (attrstring != None):
1008ec5cae07SJeff Roberson				m = attrre.match(attrstring.strip())
1009ec5cae07SJeff Roberson				if (m == None):
1010ec5cae07SJeff Roberson					break;
1011ec5cae07SJeff Roberson				#
1012ec5cae07SJeff Roberson				# Name may or may not be quoted.
1013ec5cae07SJeff Roberson				#
1014ec5cae07SJeff Roberson				# For val we have four cases:
1015ec5cae07SJeff Roberson				# 1) quotes followed by comma and more
1016ec5cae07SJeff Roberson				#    attributes.
1017ec5cae07SJeff Roberson				# 2) no quotes followed by comma and more
1018ec5cae07SJeff Roberson				#    attributes.
1019ec5cae07SJeff Roberson				# 3) no more attributes or comma with quotes.
1020ec5cae07SJeff Roberson				# 4) no more attributes or comma without quotes.
1021ec5cae07SJeff Roberson				#
1022ec5cae07SJeff Roberson				(name, name1, val, val1, attrstring, end, end1) = m.groups();
1023ec5cae07SJeff Roberson				if (name == None):
1024ec5cae07SJeff Roberson					name = name1
1025ec5cae07SJeff Roberson				if (end == None):
1026ec5cae07SJeff Roberson					end = end1
1027ec5cae07SJeff Roberson				if (val == None):
1028ec5cae07SJeff Roberson					val = val1
1029ec5cae07SJeff Roberson				if (val == None):
1030ec5cae07SJeff Roberson					val = end
1031ec5cae07SJeff Roberson				if (name == "stathz"):
1032ec5cae07SJeff Roberson					self.setstathz(val, cpu)
1033ec5cae07SJeff Roberson				attrs.append((name, val))
1034ec5cae07SJeff Roberson			args = (dat, cpu, timestamp, attrs)
1035ec5cae07SJeff Roberson			e = self.makeevent(group, id, type, args)
1036ec5cae07SJeff Roberson			if (e == None):
1037749f65e3SCraig Rodrigues				print("Unknown type", type, lineno, line, end=' ')
1038dcee3bd3SJeff Roberson
1039ec5cae07SJeff Roberson	def makeevent(self, group, id, type, args):
1040ec5cae07SJeff Roberson		e = None
1041ec5cae07SJeff Roberson		source = self.makeid(group, id, type)
1042ec5cae07SJeff Roberson		if (type == "state"):
1043ec5cae07SJeff Roberson			e = StateEvent(source, *args)
1044ec5cae07SJeff Roberson		elif (type == "counter"):
1045ec5cae07SJeff Roberson			e = CountEvent(source, *args)
1046ec5cae07SJeff Roberson		elif (type == "point"):
1047ec5cae07SJeff Roberson			e = PointEvent(source, *args)
1048ec5cae07SJeff Roberson		if (e != None):
1049932f0fa2SJeff Roberson			self.eventcnt += 1
1050ec5cae07SJeff Roberson			source.addevent(e);
1051ec5cae07SJeff Roberson		return e
1052dcee3bd3SJeff Roberson
1053ec5cae07SJeff Roberson	def setstathz(self, val, cpu):
1054ec5cae07SJeff Roberson		self.stathz = int(val)
1055dcee3bd3SJeff Roberson		cpu = int(cpu)
1056dcee3bd3SJeff Roberson		try:
1057dcee3bd3SJeff Roberson			ticks = self.ticks[cpu]
1058dcee3bd3SJeff Roberson		except:
1059dcee3bd3SJeff Roberson			self.ticks[cpu] = 0
1060dcee3bd3SJeff Roberson		self.ticks[cpu] += 1
1061dcee3bd3SJeff Roberson
1062ec5cae07SJeff Roberson	def checkstamp(self, timestamp):
1063ec5cae07SJeff Roberson		timestamp = int(timestamp)
1064ec5cae07SJeff Roberson		if (self.timestamp_f == None):
1065ec5cae07SJeff Roberson			self.timestamp_f = timestamp;
106616ef0f3bSJeff Roberson		if (self.timestamp_l != None and
106716ef0f3bSJeff Roberson		    timestamp -2048> self.timestamp_l):
1068ec5cae07SJeff Roberson			return (0)
1069ec5cae07SJeff Roberson		self.timestamp_l = timestamp;
1070ec5cae07SJeff Roberson		return (1)
1071dcee3bd3SJeff Roberson
1072ec5cae07SJeff Roberson	def makeid(self, group, id, type):
107316ef0f3bSJeff Roberson		tag = group + id
1074aef675d8SCraig Rodrigues		if (tag in self.taghash):
107516ef0f3bSJeff Roberson			return self.taghash[tag]
1076ec5cae07SJeff Roberson		if (type == "counter"):
1077ec5cae07SJeff Roberson			source = Counter(group, id)
10780199a61dSJohn Baldwin		else:
1079ec5cae07SJeff Roberson			source = EventSource(group, id)
1080ec5cae07SJeff Roberson		sources.append(source)
108116ef0f3bSJeff Roberson		self.taghash[tag] = source
1082ec5cae07SJeff Roberson		return (source)
10830199a61dSJohn Baldwin
1084ec5cae07SJeff Roberson	def findid(self, id):
1085ec5cae07SJeff Roberson		for source in sources:
1086ec5cae07SJeff Roberson			if (source.name == id):
1087ec5cae07SJeff Roberson				return source
1088ec5cae07SJeff Roberson		return (None)
10890199a61dSJohn Baldwin
1090ec5cae07SJeff Roberson	def timespan(self):
1091ec5cae07SJeff Roberson		return (self.timestamp_f - self.timestamp_l);
10920199a61dSJohn Baldwin
1093ec5cae07SJeff Roberson	def ticksps(self):
1094ec5cae07SJeff Roberson		oneghz = 1000000000
1095ec5cae07SJeff Roberson		# Use user supplied clock first
1096ec5cae07SJeff Roberson		if (clockfreq != None):
1097ec5cae07SJeff Roberson			return int(clockfreq * oneghz)
10980199a61dSJohn Baldwin
1099ec5cae07SJeff Roberson		# Check for a discovered clock
110016ef0f3bSJeff Roberson		if (self.stathz != 0):
1101ec5cae07SJeff Roberson			return (self.timespan() / self.ticks[0]) * int(self.stathz)
1102ec5cae07SJeff Roberson		# Pretend we have a 1ns clock
1103749f65e3SCraig Rodrigues		print("WARNING: No clock discovered and no frequency ", end=' ')
1104749f65e3SCraig Rodrigues		print("specified via the command line.")
1105749f65e3SCraig Rodrigues		print("Using fake 1ghz clock")
1106ec5cae07SJeff Roberson		return (oneghz);
1107dcee3bd3SJeff Roberson
1108dcee3bd3SJeff Roberson	def fixup(self):
1109ec5cae07SJeff Roberson		for source in sources:
1110ec5cae07SJeff Roberson			e = PadEvent(source, -1, self.timestamp_l)
1111ec5cae07SJeff Roberson			source.addevent(e)
1112ec5cae07SJeff Roberson			e = PadEvent(source, -1, self.timestamp_f, last=1)
1113ec5cae07SJeff Roberson			source.addlastevent(e)
1114dcee3bd3SJeff Roberson			source.fixup()
1115ec5cae07SJeff Roberson		sources.sort()
1116ec5cae07SJeff Roberson
1117ec5cae07SJeff Robersonclass SchedNames(Canvas):
1118ec5cae07SJeff Roberson	def __init__(self, master, display):
1119ec5cae07SJeff Roberson		self.display = display
1120ec5cae07SJeff Roberson		self.parent = master
1121ec5cae07SJeff Roberson		self.bdheight = master.bdheight
1122ec5cae07SJeff Roberson		self.items = {}
1123ec5cae07SJeff Roberson		self.ysize = 0
1124ec5cae07SJeff Roberson		self.lines = []
1125ec5cae07SJeff Roberson		Canvas.__init__(self, master, width=120,
1126ec5cae07SJeff Roberson		    height=display["height"], bg='grey',
1127ec5cae07SJeff Roberson		    scrollregion=(0, 0, 50, 100))
1128ec5cae07SJeff Roberson
1129ec5cae07SJeff Roberson	def moveline(self, cur_y, y):
1130ec5cae07SJeff Roberson		for line in self.lines:
1131ec5cae07SJeff Roberson			(x0, y0, x1, y1) = self.coords(line)
1132ec5cae07SJeff Roberson			if (cur_y != y0):
1133ec5cae07SJeff Roberson				continue
1134ec5cae07SJeff Roberson			self.move(line, 0, y)
1135ec5cae07SJeff Roberson			return
1136ec5cae07SJeff Roberson
1137ec5cae07SJeff Roberson	def draw(self):
1138ec5cae07SJeff Roberson		status.startup("Drawing names")
1139ec5cae07SJeff Roberson		ypos = 0
1140ec5cae07SJeff Roberson		self.configure(scrollregion=(0, 0,
1141ec5cae07SJeff Roberson		    self["width"], self.display.ysize()))
1142ec5cae07SJeff Roberson		for source in sources:
1143ec5cae07SJeff Roberson			l = self.create_line(0, ypos, self["width"], ypos,
1144ec5cae07SJeff Roberson			    width=1, fill="black", tags=("all","sources"))
1145ec5cae07SJeff Roberson			self.lines.append(l)
1146ec5cae07SJeff Roberson			ypos += self.bdheight
1147ec5cae07SJeff Roberson			ypos += source.ysize()
1148ec5cae07SJeff Roberson			t = source.drawname(self, ypos)
1149ec5cae07SJeff Roberson			self.items[t] = source
1150ec5cae07SJeff Roberson			ypos += self.bdheight
1151ec5cae07SJeff Roberson		self.ysize = ypos
1152ec5cae07SJeff Roberson		self.create_line(0, ypos, self["width"], ypos,
1153ec5cae07SJeff Roberson		    width=1, fill="black", tags=("all",))
1154ec5cae07SJeff Roberson		self.bind("<Button-1>", self.master.mousepress);
115550d670daSJeff Roberson		self.bind("<Button-3>", self.master.mousepressright);
1156ec5cae07SJeff Roberson		self.bind("<ButtonRelease-1>", self.master.mouserelease);
1157ec5cae07SJeff Roberson		self.bind("<B1-Motion>", self.master.mousemotion);
1158ec5cae07SJeff Roberson
11593d21f0f4SJeff Roberson	def updatescroll(self):
11603d21f0f4SJeff Roberson		self.configure(scrollregion=(0, 0,
11613d21f0f4SJeff Roberson		    self["width"], self.display.ysize()))
11623d21f0f4SJeff Roberson
1163dcee3bd3SJeff Roberson
1164dcee3bd3SJeff Robersonclass SchedDisplay(Canvas):
1165dcee3bd3SJeff Roberson	def __init__(self, master):
11662977b8f9SJohn Baldwin		self.ratio = 1
11672977b8f9SJohn Baldwin		self.parent = master
1168ec5cae07SJeff Roberson		self.bdheight = master.bdheight
1169ec5cae07SJeff Roberson		self.items = {}
1170ec5cae07SJeff Roberson		self.lines = []
1171dcee3bd3SJeff Roberson		Canvas.__init__(self, master, width=800, height=500, bg='grey',
1172dcee3bd3SJeff Roberson		     scrollregion=(0, 0, 800, 500))
1173dcee3bd3SJeff Roberson
1174ec5cae07SJeff Roberson	def prepare(self):
1175ec5cae07SJeff Roberson		#
11762977b8f9SJohn Baldwin		# Compute a ratio to ensure that the file's timespan fits into
11772977b8f9SJohn Baldwin		# 2^31.  Although python may handle larger values for X
11782977b8f9SJohn Baldwin		# values, the Tk internals do not.
1179ec5cae07SJeff Roberson		#
11802977b8f9SJohn Baldwin		self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1
11812977b8f9SJohn Baldwin
1182dcee3bd3SJeff Roberson	def draw(self):
1183dcee3bd3SJeff Roberson		ypos = 0
1184dcee3bd3SJeff Roberson		xsize = self.xsize()
1185ec5cae07SJeff Roberson		for source in sources:
1186dcee3bd3SJeff Roberson			status.startup("Drawing " + source.name)
1187ec5cae07SJeff Roberson			l = self.create_line(0, ypos, xsize, ypos,
1188dcee3bd3SJeff Roberson			    width=1, fill="black", tags=("all",))
1189ec5cae07SJeff Roberson			self.lines.append(l)
1190dcee3bd3SJeff Roberson			ypos += self.bdheight
1191dcee3bd3SJeff Roberson			ypos += source.ysize()
1192dcee3bd3SJeff Roberson			source.draw(self, ypos)
1193dcee3bd3SJeff Roberson			ypos += self.bdheight
1194dcee3bd3SJeff Roberson		self.tag_raise("point", "state")
1195932f0fa2SJeff Roberson		self.tag_lower("cpubg", ALL)
1196dcee3bd3SJeff Roberson		self.create_line(0, ypos, xsize, ypos,
1197932f0fa2SJeff Roberson		    width=1, fill="black", tags=("lines",))
1198dcee3bd3SJeff Roberson		self.tag_bind("event", "<Enter>", self.mouseenter)
1199dcee3bd3SJeff Roberson		self.tag_bind("event", "<Leave>", self.mouseexit)
1200ec5cae07SJeff Roberson		self.bind("<Button-1>", self.mousepress)
120150d670daSJeff Roberson		self.bind("<Button-3>", self.master.mousepressright);
12022977b8f9SJohn Baldwin		self.bind("<Button-4>", self.wheelup)
12032977b8f9SJohn Baldwin		self.bind("<Button-5>", self.wheeldown)
1204ec5cae07SJeff Roberson		self.bind("<ButtonRelease-1>", self.master.mouserelease);
1205ec5cae07SJeff Roberson		self.bind("<B1-Motion>", self.master.mousemotion);
1206ec5cae07SJeff Roberson
1207ec5cae07SJeff Roberson	def moveline(self, cur_y, y):
1208ec5cae07SJeff Roberson		for line in self.lines:
1209ec5cae07SJeff Roberson			(x0, y0, x1, y1) = self.coords(line)
1210ec5cae07SJeff Roberson			if (cur_y != y0):
1211ec5cae07SJeff Roberson				continue
1212ec5cae07SJeff Roberson			self.move(line, 0, y)
1213ec5cae07SJeff Roberson			return
1214dcee3bd3SJeff Roberson
1215dcee3bd3SJeff Roberson	def mouseenter(self, event):
1216dcee3bd3SJeff Roberson		item, = self.find_withtag(CURRENT)
1217ec5cae07SJeff Roberson		self.items[item].mouseenter(self)
1218dcee3bd3SJeff Roberson
1219dcee3bd3SJeff Roberson	def mouseexit(self, event):
1220dcee3bd3SJeff Roberson		item, = self.find_withtag(CURRENT)
1221ec5cae07SJeff Roberson		self.items[item].mouseexit(self)
1222dcee3bd3SJeff Roberson
1223dcee3bd3SJeff Roberson	def mousepress(self, event):
1224ec5cae07SJeff Roberson		# Find out what's beneath us
1225ec5cae07SJeff Roberson		items = self.find_withtag(CURRENT)
1226ec5cae07SJeff Roberson		if (len(items) == 0):
1227ec5cae07SJeff Roberson			self.master.mousepress(event)
1228ec5cae07SJeff Roberson			return
1229ec5cae07SJeff Roberson		# Only grab mouse presses for things with event tags.
1230ec5cae07SJeff Roberson		item = items[0]
1231ec5cae07SJeff Roberson		tags = self.gettags(item)
1232ec5cae07SJeff Roberson		for tag in tags:
1233ec5cae07SJeff Roberson			if (tag == "event"):
1234ec5cae07SJeff Roberson				self.items[item].mousepress(self)
1235ec5cae07SJeff Roberson				return
1236ec5cae07SJeff Roberson		# Leave the rest to the master window
1237ec5cae07SJeff Roberson		self.master.mousepress(event)
1238dcee3bd3SJeff Roberson
12392977b8f9SJohn Baldwin	def wheeldown(self, event):
12402977b8f9SJohn Baldwin		self.parent.display_yview("scroll", 1, "units")
12412977b8f9SJohn Baldwin
12422977b8f9SJohn Baldwin	def wheelup(self, event):
12432977b8f9SJohn Baldwin		self.parent.display_yview("scroll", -1, "units")
12442977b8f9SJohn Baldwin
1245dcee3bd3SJeff Roberson	def xsize(self):
1246932f0fa2SJeff Roberson		return ((ktrfile.timespan() / self.ratio) + (X_BORDER * 2))
1247dcee3bd3SJeff Roberson
1248dcee3bd3SJeff Roberson	def ysize(self):
1249dcee3bd3SJeff Roberson		ysize = 0
1250ec5cae07SJeff Roberson		for source in sources:
12513d21f0f4SJeff Roberson			if (source.hidden == 1):
12523d21f0f4SJeff Roberson				continue
12533d21f0f4SJeff Roberson			ysize += self.parent.sourcesize(source)
1254ec5cae07SJeff Roberson		return ysize
1255dcee3bd3SJeff Roberson
1256dcee3bd3SJeff Roberson	def scaleset(self, ratio):
1257ec5cae07SJeff Roberson		if (ktrfile == None):
1258dcee3bd3SJeff Roberson			return
1259dcee3bd3SJeff Roberson		oldratio = self.ratio
1260ec5cae07SJeff Roberson		xstart, xend = self.xview()
1261ec5cae07SJeff Roberson		midpoint = xstart + ((xend - xstart) / 2)
1262dcee3bd3SJeff Roberson
1263dcee3bd3SJeff Roberson		self.ratio = ratio
12643d21f0f4SJeff Roberson		self.updatescroll()
1265932f0fa2SJeff Roberson		self.scale(ALL, 0, 0, float(oldratio) / ratio, 1)
1266dcee3bd3SJeff Roberson
1267ec5cae07SJeff Roberson		xstart, xend = self.xview()
1268ec5cae07SJeff Roberson		xsize = (xend - xstart) / 2
1269ec5cae07SJeff Roberson		self.xview_moveto(midpoint - xsize)
1270dcee3bd3SJeff Roberson
12713d21f0f4SJeff Roberson	def updatescroll(self):
12723d21f0f4SJeff Roberson		self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
12733d21f0f4SJeff Roberson
1274dcee3bd3SJeff Roberson	def scaleget(self):
1275dcee3bd3SJeff Roberson		return self.ratio
1276dcee3bd3SJeff Roberson
1277ec5cae07SJeff Roberson	def getcolor(self, tag):
1278ec5cae07SJeff Roberson		return self.itemcget(tag, "fill")
1279ec5cae07SJeff Roberson
1280ec5cae07SJeff Roberson	def getstate(self, tag):
1281ec5cae07SJeff Roberson		return self.itemcget(tag, "state")
1282ec5cae07SJeff Roberson
1283dcee3bd3SJeff Roberson	def setcolor(self, tag, color):
1284dcee3bd3SJeff Roberson		self.itemconfigure(tag, state="normal", fill=color)
1285dcee3bd3SJeff Roberson
1286dcee3bd3SJeff Roberson	def hide(self, tag):
1287dcee3bd3SJeff Roberson		self.itemconfigure(tag, state="hidden")
1288dcee3bd3SJeff Roberson
1289dcee3bd3SJeff Robersonclass GraphMenu(Frame):
1290dcee3bd3SJeff Roberson	def __init__(self, master):
1291dcee3bd3SJeff Roberson		Frame.__init__(self, master, bd=2, relief=RAISED)
129250d670daSJeff Roberson		self.conf = Menubutton(self, text="Configure")
129350d670daSJeff Roberson		self.confmenu = Menu(self.conf, tearoff=0)
129450d670daSJeff Roberson		self.confmenu.add_command(label="Event Colors",
1295dcee3bd3SJeff Roberson		    command=self.econf)
129650d670daSJeff Roberson		self.confmenu.add_command(label="CPU Colors",
1297ec5cae07SJeff Roberson		    command=self.cconf)
129850d670daSJeff Roberson		self.confmenu.add_command(label="Source Configure",
12993d21f0f4SJeff Roberson		    command=self.sconf)
130050d670daSJeff Roberson		self.conf["menu"] = self.confmenu
130150d670daSJeff Roberson		self.conf.pack(side=LEFT)
1302dcee3bd3SJeff Roberson
1303dcee3bd3SJeff Roberson	def econf(self):
1304ec5cae07SJeff Roberson		ColorConfigure(eventcolors, "Event Display Configuration")
1305ec5cae07SJeff Roberson
1306ec5cae07SJeff Roberson	def cconf(self):
1307ec5cae07SJeff Roberson		ColorConfigure(cpucolors, "CPU Background Colors")
1308dcee3bd3SJeff Roberson
13093d21f0f4SJeff Roberson	def sconf(self):
13103d21f0f4SJeff Roberson		SourceConfigure()
13113d21f0f4SJeff Roberson
1312dcee3bd3SJeff Robersonclass SchedGraph(Frame):
1313dcee3bd3SJeff Roberson	def __init__(self, master):
1314dcee3bd3SJeff Roberson		Frame.__init__(self, master)
1315dcee3bd3SJeff Roberson		self.menu = None
1316dcee3bd3SJeff Roberson		self.names = None
1317dcee3bd3SJeff Roberson		self.display = None
1318dcee3bd3SJeff Roberson		self.scale = None
1319dcee3bd3SJeff Roberson		self.status = None
1320932f0fa2SJeff Roberson		self.bdheight = Y_BORDER
1321ec5cae07SJeff Roberson		self.clicksource = None
1322ec5cae07SJeff Roberson		self.lastsource = None
1323dcee3bd3SJeff Roberson		self.pack(expand=1, fill="both")
1324dcee3bd3SJeff Roberson		self.buildwidgets()
1325dcee3bd3SJeff Roberson		self.layout()
13269799411bSJohn Baldwin		self.bind_all("<Control-q>", self.quitcb)
13279799411bSJohn Baldwin
13289799411bSJohn Baldwin	def quitcb(self, event):
13299799411bSJohn Baldwin		self.quit()
1330dcee3bd3SJeff Roberson
1331dcee3bd3SJeff Roberson	def buildwidgets(self):
1332dcee3bd3SJeff Roberson		global status
1333dcee3bd3SJeff Roberson		self.menu = GraphMenu(self)
1334dcee3bd3SJeff Roberson		self.display = SchedDisplay(self)
1335ec5cae07SJeff Roberson		self.names = SchedNames(self, self.display)
1336dcee3bd3SJeff Roberson		self.scale = Scaler(self, self.display)
1337dcee3bd3SJeff Roberson		status = self.status = Status(self)
1338dcee3bd3SJeff Roberson		self.scrollY = Scrollbar(self, orient="vertical",
1339dcee3bd3SJeff Roberson		    command=self.display_yview)
1340dcee3bd3SJeff Roberson		self.display.scrollX = Scrollbar(self, orient="horizontal",
1341dcee3bd3SJeff Roberson		    command=self.display.xview)
1342dcee3bd3SJeff Roberson		self.display["xscrollcommand"] = self.display.scrollX.set
1343dcee3bd3SJeff Roberson		self.display["yscrollcommand"] = self.scrollY.set
1344dcee3bd3SJeff Roberson		self.names["yscrollcommand"] = self.scrollY.set
1345dcee3bd3SJeff Roberson
1346dcee3bd3SJeff Roberson	def layout(self):
1347dcee3bd3SJeff Roberson		self.columnconfigure(1, weight=1)
1348dcee3bd3SJeff Roberson		self.rowconfigure(1, weight=1)
1349dcee3bd3SJeff Roberson		self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1350dcee3bd3SJeff Roberson		self.names.grid(row=1, column=0, sticky=N+S)
1351dcee3bd3SJeff Roberson		self.display.grid(row=1, column=1, sticky=W+E+N+S)
1352dcee3bd3SJeff Roberson		self.scrollY.grid(row=1, column=2, sticky=N+S)
1353dcee3bd3SJeff Roberson		self.display.scrollX.grid(row=2, column=0, columnspan=2,
1354dcee3bd3SJeff Roberson		    sticky=E+W)
1355dcee3bd3SJeff Roberson		self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1356dcee3bd3SJeff Roberson		self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1357dcee3bd3SJeff Roberson
1358ec5cae07SJeff Roberson	def draw(self):
1359dcee3bd3SJeff Roberson		self.master.update()
1360ec5cae07SJeff Roberson		self.display.prepare()
1361ec5cae07SJeff Roberson		self.names.draw()
1362dcee3bd3SJeff Roberson		self.display.draw()
1363ec5cae07SJeff Roberson		self.status.startup("")
136416ef0f3bSJeff Roberson		#
136516ef0f3bSJeff Roberson		# Configure scale related values
136616ef0f3bSJeff Roberson		#
136716ef0f3bSJeff Roberson		scalemax = ktrfile.timespan() / int(self.display["width"])
136816ef0f3bSJeff Roberson		width = int(root.geometry().split('x')[0])
136916ef0f3bSJeff Roberson		self.constwidth = width - int(self.display["width"])
137016ef0f3bSJeff Roberson		self.scale.setmax(scalemax)
137116ef0f3bSJeff Roberson		self.scale.set(scalemax)
1372dcee3bd3SJeff Roberson		self.display.xview_moveto(0)
137316ef0f3bSJeff Roberson		self.bind("<Configure>", self.resize)
1374dcee3bd3SJeff Roberson
1375ec5cae07SJeff Roberson	def mousepress(self, event):
1376ec5cae07SJeff Roberson		self.clicksource = self.sourceat(event.y)
1377ec5cae07SJeff Roberson
137850d670daSJeff Roberson	def mousepressright(self, event):
137950d670daSJeff Roberson		source = self.sourceat(event.y)
138050d670daSJeff Roberson		if (source == None):
138150d670daSJeff Roberson			return
138250d670daSJeff Roberson		SourceContext(event, source)
138350d670daSJeff Roberson
1384ec5cae07SJeff Roberson	def mouserelease(self, event):
1385ec5cae07SJeff Roberson		if (self.clicksource == None):
1386ec5cae07SJeff Roberson			return
1387ec5cae07SJeff Roberson		newsource = self.sourceat(event.y)
1388ec5cae07SJeff Roberson		if (self.clicksource != newsource):
1389ec5cae07SJeff Roberson			self.sourceswap(self.clicksource, newsource)
1390ec5cae07SJeff Roberson		self.clicksource = None
1391ec5cae07SJeff Roberson		self.lastsource = None
1392ec5cae07SJeff Roberson
1393ec5cae07SJeff Roberson	def mousemotion(self, event):
1394ec5cae07SJeff Roberson		if (self.clicksource == None):
1395ec5cae07SJeff Roberson			return
1396ec5cae07SJeff Roberson		newsource = self.sourceat(event.y)
1397ec5cae07SJeff Roberson		#
1398ec5cae07SJeff Roberson		# If we get a None source they moved off the page.
1399ec5cae07SJeff Roberson		# swapsource() can't handle moving multiple items so just
1400ec5cae07SJeff Roberson		# pretend we never clicked on anything to begin with so the
1401ec5cae07SJeff Roberson		# user can't mouseover a non-contiguous area.
1402ec5cae07SJeff Roberson		#
1403ec5cae07SJeff Roberson		if (newsource == None):
1404ec5cae07SJeff Roberson			self.clicksource = None
1405ec5cae07SJeff Roberson			self.lastsource = None
1406ec5cae07SJeff Roberson			return
1407ec5cae07SJeff Roberson		if (newsource == self.lastsource):
1408ec5cae07SJeff Roberson			return;
1409ec5cae07SJeff Roberson		self.lastsource = newsource
1410ec5cae07SJeff Roberson		if (newsource != self.clicksource):
1411ec5cae07SJeff Roberson			self.sourceswap(self.clicksource, newsource)
1412ec5cae07SJeff Roberson
1413ec5cae07SJeff Roberson	# These are here because this object controls layout
1414ec5cae07SJeff Roberson	def sourcestart(self, source):
1415ec5cae07SJeff Roberson		return source.y - self.bdheight - source.ysize()
1416ec5cae07SJeff Roberson
1417ec5cae07SJeff Roberson	def sourceend(self, source):
1418ec5cae07SJeff Roberson		return source.y + self.bdheight
1419ec5cae07SJeff Roberson
1420ec5cae07SJeff Roberson	def sourcesize(self, source):
1421ec5cae07SJeff Roberson		return (self.bdheight * 2) + source.ysize()
1422ec5cae07SJeff Roberson
1423ec5cae07SJeff Roberson	def sourceswap(self, source1, source2):
1424ec5cae07SJeff Roberson		# Sort so we always know which one is on top.
1425ec5cae07SJeff Roberson		if (source2.y < source1.y):
1426ec5cae07SJeff Roberson			swap = source1
1427ec5cae07SJeff Roberson			source1 = source2
1428ec5cae07SJeff Roberson			source2 = swap
1429ec5cae07SJeff Roberson		# Only swap adjacent sources
1430ec5cae07SJeff Roberson		if (self.sourceend(source1) != self.sourcestart(source2)):
1431ec5cae07SJeff Roberson			return
1432ec5cae07SJeff Roberson		# Compute start coordinates and target coordinates
1433ec5cae07SJeff Roberson		y1 = self.sourcestart(source1)
1434ec5cae07SJeff Roberson		y2 = self.sourcestart(source2)
1435ec5cae07SJeff Roberson		y1targ = y1 + self.sourcesize(source2)
1436ec5cae07SJeff Roberson		y2targ = y1
1437ec5cae07SJeff Roberson		#
1438ec5cae07SJeff Roberson		# If the sizes are not equal, adjust the start of the lower
1439ec5cae07SJeff Roberson		# source to account for the lost/gained space.
1440ec5cae07SJeff Roberson		#
1441ec5cae07SJeff Roberson		if (source1.ysize() != source2.ysize()):
1442ec5cae07SJeff Roberson			diff = source2.ysize() - source1.ysize()
1443ec5cae07SJeff Roberson			self.names.moveline(y2, diff);
1444ec5cae07SJeff Roberson			self.display.moveline(y2, diff)
1445ec5cae07SJeff Roberson		source1.move(self.display, 0, y1targ - y1)
1446ec5cae07SJeff Roberson		source2.move(self.display, 0, y2targ - y2)
1447ec5cae07SJeff Roberson		source1.movename(self.names, 0, y1targ - y1)
1448ec5cae07SJeff Roberson		source2.movename(self.names, 0, y2targ - y2)
1449ec5cae07SJeff Roberson
145050d670daSJeff Roberson	def sourcepicky(self, source):
14513d21f0f4SJeff Roberson		if (source.hidden == 0):
145250d670daSJeff Roberson			return self.sourcestart(source)
145350d670daSJeff Roberson		# Revert to group based sort
145450d670daSJeff Roberson		sources.sort()
14553d21f0f4SJeff Roberson		prev = None
14563d21f0f4SJeff Roberson		for s in sources:
14573d21f0f4SJeff Roberson			if (s == source):
14583d21f0f4SJeff Roberson				break
14593d21f0f4SJeff Roberson			if (s.hidden == 0):
14603d21f0f4SJeff Roberson				prev = s
14613d21f0f4SJeff Roberson		if (prev == None):
14623d21f0f4SJeff Roberson			newy = 0
14633d21f0f4SJeff Roberson		else:
14643d21f0f4SJeff Roberson			newy = self.sourcestart(prev) + self.sourcesize(prev)
146550d670daSJeff Roberson		return newy
146650d670daSJeff Roberson
146750d670daSJeff Roberson	def sourceshow(self, source):
146850d670daSJeff Roberson		if (source.hidden == 0):
146950d670daSJeff Roberson			return;
147050d670daSJeff Roberson		newy = self.sourcepicky(source)
14713d21f0f4SJeff Roberson		off = newy - self.sourcestart(source)
147250d670daSJeff Roberson		self.sourceshiftall(newy-1, self.sourcesize(source))
14733d21f0f4SJeff Roberson		self.sourceshift(source, off)
14743d21f0f4SJeff Roberson		source.hidden = 0
14753d21f0f4SJeff Roberson
147650d670daSJeff Roberson	#
147750d670daSJeff Roberson	# Optimized source show of multiple entries that only moves each
147850d670daSJeff Roberson	# existing entry once.  Doing sourceshow() iteratively is too
147950d670daSJeff Roberson	# expensive due to python's canvas.move().
148050d670daSJeff Roberson	#
148150d670daSJeff Roberson	def sourceshowlist(self, srclist):
1482*7e8ed296SAndriy Gapon		srclist.sort(key=attrgetter('y'))
148350d670daSJeff Roberson		startsize = []
148450d670daSJeff Roberson		for source in srclist:
148550d670daSJeff Roberson			if (source.hidden == 0):
148650d670daSJeff Roberson				srclist.remove(source)
148750d670daSJeff Roberson			startsize.append((self.sourcepicky(source),
148850d670daSJeff Roberson			    self.sourcesize(source)))
148950d670daSJeff Roberson
1490*7e8ed296SAndriy Gapon		sources.sort(key=attrgetter('y'), reverse=True)
149150d670daSJeff Roberson		self.status.startup("Updating display...");
149250d670daSJeff Roberson		for source in sources:
149350d670daSJeff Roberson			if (source.hidden == 1):
149450d670daSJeff Roberson				continue
149550d670daSJeff Roberson			nstart = self.sourcestart(source)
149650d670daSJeff Roberson			size = 0
149750d670daSJeff Roberson			for hidden in startsize:
149850d670daSJeff Roberson				(start, sz) = hidden
149950d670daSJeff Roberson				if (start <= nstart or start+sz <= nstart):
150050d670daSJeff Roberson					size += sz
150150d670daSJeff Roberson			self.sourceshift(source, size)
150250d670daSJeff Roberson		idx = 0
150350d670daSJeff Roberson		size = 0
150450d670daSJeff Roberson		for source in srclist:
150550d670daSJeff Roberson			(newy, sz) = startsize[idx]
150650d670daSJeff Roberson			off = (newy + size) - self.sourcestart(source)
150750d670daSJeff Roberson			self.sourceshift(source, off)
150850d670daSJeff Roberson			source.hidden = 0
150950d670daSJeff Roberson			size += sz
151050d670daSJeff Roberson			idx += 1
151116ef0f3bSJeff Roberson		self.updatescroll()
151250d670daSJeff Roberson		self.status.set("")
151350d670daSJeff Roberson
151450d670daSJeff Roberson	#
151550d670daSJeff Roberson	# Optimized source hide of multiple entries that only moves each
151650d670daSJeff Roberson	# remaining entry once.  Doing sourcehide() iteratively is too
151750d670daSJeff Roberson	# expensive due to python's canvas.move().
151850d670daSJeff Roberson	#
151950d670daSJeff Roberson	def sourcehidelist(self, srclist):
1520*7e8ed296SAndriy Gapon		srclist.sort(key=attrgetter('y'))
1521*7e8ed296SAndriy Gapon		sources.sort(key=attrgetter('y'))
152250d670daSJeff Roberson		startsize = []
152350d670daSJeff Roberson		off = len(sources) * 100
152450d670daSJeff Roberson		self.status.startup("Updating display...");
152550d670daSJeff Roberson		for source in srclist:
152650d670daSJeff Roberson			if (source.hidden == 1):
152750d670daSJeff Roberson				srclist.remove(source)
152850d670daSJeff Roberson			#
152950d670daSJeff Roberson			# Remember our old position so we can sort things
153050d670daSJeff Roberson			# below us when we're done.
153150d670daSJeff Roberson			#
153250d670daSJeff Roberson			startsize.append((self.sourcestart(source),
153350d670daSJeff Roberson			    self.sourcesize(source)))
153450d670daSJeff Roberson			self.sourceshift(source, off)
153550d670daSJeff Roberson			source.hidden = 1
153650d670daSJeff Roberson
153750d670daSJeff Roberson		idx = 0
153850d670daSJeff Roberson		size = 0
153950d670daSJeff Roberson		for hidden in startsize:
154050d670daSJeff Roberson			(start, sz) = hidden
154150d670daSJeff Roberson			size += sz
154250d670daSJeff Roberson			if (idx + 1 < len(startsize)):
154350d670daSJeff Roberson				(stop, sz) = startsize[idx+1]
154450d670daSJeff Roberson			else:
154550d670daSJeff Roberson				stop = self.display.ysize()
154650d670daSJeff Roberson			idx += 1
154750d670daSJeff Roberson			for source in sources:
154850d670daSJeff Roberson				nstart = self.sourcestart(source)
154950d670daSJeff Roberson				if (nstart < start or source.hidden == 1):
155050d670daSJeff Roberson					continue
155150d670daSJeff Roberson				if (nstart >= stop):
155250d670daSJeff Roberson					break;
155350d670daSJeff Roberson				self.sourceshift(source, -size)
155416ef0f3bSJeff Roberson		self.updatescroll()
155550d670daSJeff Roberson		self.status.set("")
155650d670daSJeff Roberson
15573d21f0f4SJeff Roberson	def sourcehide(self, source):
155850d670daSJeff Roberson		if (source.hidden == 1):
155950d670daSJeff Roberson			return;
15603d21f0f4SJeff Roberson		# Move it out of the visible area
15613d21f0f4SJeff Roberson		off = len(sources) * 100
15623d21f0f4SJeff Roberson		start = self.sourcestart(source)
15633d21f0f4SJeff Roberson		self.sourceshift(source, off)
15643d21f0f4SJeff Roberson		self.sourceshiftall(start, -self.sourcesize(source))
15653d21f0f4SJeff Roberson		source.hidden = 1
15663d21f0f4SJeff Roberson
15673d21f0f4SJeff Roberson	def sourceshift(self, source, off):
15683d21f0f4SJeff Roberson		start = self.sourcestart(source)
15693d21f0f4SJeff Roberson		source.move(self.display, 0, off)
15703d21f0f4SJeff Roberson		source.movename(self.names, 0, off)
15713d21f0f4SJeff Roberson		self.names.moveline(start, off);
15723d21f0f4SJeff Roberson		self.display.moveline(start, off)
157350d670daSJeff Roberson		#
157450d670daSJeff Roberson		# We update the idle tasks to shrink the dirtied area so
157550d670daSJeff Roberson		# it does not always include the entire screen.
157650d670daSJeff Roberson		#
157750d670daSJeff Roberson		self.names.update_idletasks()
157850d670daSJeff Roberson		self.display.update_idletasks()
15793d21f0f4SJeff Roberson
15803d21f0f4SJeff Roberson	def sourceshiftall(self, start, off):
158150d670daSJeff Roberson		self.status.startup("Updating display...");
15823d21f0f4SJeff Roberson		for source in sources:
15833d21f0f4SJeff Roberson			nstart = self.sourcestart(source)
15843d21f0f4SJeff Roberson			if (nstart < start):
15853d21f0f4SJeff Roberson				continue;
15863d21f0f4SJeff Roberson			self.sourceshift(source, off)
158716ef0f3bSJeff Roberson		self.updatescroll()
158850d670daSJeff Roberson		self.status.set("")
15893d21f0f4SJeff Roberson
1590ec5cae07SJeff Roberson	def sourceat(self, ypos):
1591ec5cae07SJeff Roberson		(start, end) = self.names.yview()
1592ec5cae07SJeff Roberson		starty = start * float(self.names.ysize)
1593ec5cae07SJeff Roberson		ypos += starty
1594ec5cae07SJeff Roberson		for source in sources:
15953d21f0f4SJeff Roberson			if (source.hidden == 1):
15963d21f0f4SJeff Roberson				continue;
1597ec5cae07SJeff Roberson			yend = self.sourceend(source)
1598ec5cae07SJeff Roberson			ystart = self.sourcestart(source)
1599ec5cae07SJeff Roberson			if (ypos >= ystart and ypos <= yend):
1600ec5cae07SJeff Roberson				return source
1601ec5cae07SJeff Roberson		return None
1602ec5cae07SJeff Roberson
1603dcee3bd3SJeff Roberson	def display_yview(self, *args):
1604dcee3bd3SJeff Roberson		self.names.yview(*args)
1605dcee3bd3SJeff Roberson		self.display.yview(*args)
1606dcee3bd3SJeff Roberson
160716ef0f3bSJeff Roberson	def resize(self, *args):
160816ef0f3bSJeff Roberson		width = int(root.geometry().split('x')[0])
160916ef0f3bSJeff Roberson		scalemax = ktrfile.timespan() / (width - self.constwidth)
161016ef0f3bSJeff Roberson		self.scale.setmax(scalemax)
161116ef0f3bSJeff Roberson
161216ef0f3bSJeff Roberson	def updatescroll(self):
161316ef0f3bSJeff Roberson		self.names.updatescroll()
161416ef0f3bSJeff Roberson		self.display.updatescroll()
161516ef0f3bSJeff Roberson
1616dcee3bd3SJeff Roberson	def setcolor(self, tag, color):
1617dcee3bd3SJeff Roberson		self.display.setcolor(tag, color)
1618dcee3bd3SJeff Roberson
1619dcee3bd3SJeff Roberson	def hide(self, tag):
1620dcee3bd3SJeff Roberson		self.display.hide(tag)
1621dcee3bd3SJeff Roberson
1622ec5cae07SJeff Roberson	def getcolor(self, tag):
1623ec5cae07SJeff Roberson		return self.display.getcolor(tag)
1624ec5cae07SJeff Roberson
1625ec5cae07SJeff Roberson	def getstate(self, tag):
1626ec5cae07SJeff Roberson		return self.display.getstate(tag)
1627ec5cae07SJeff Roberson
1628ec5cae07SJeff Robersonif (len(sys.argv) != 2 and len(sys.argv) != 3):
1629749f65e3SCraig Rodrigues	print("usage:", sys.argv[0], "<ktr file> [clock freq in ghz]")
1630dcee3bd3SJeff Roberson	sys.exit(1)
1631dcee3bd3SJeff Roberson
1632ec5cae07SJeff Robersonif (len(sys.argv) > 2):
1633ec5cae07SJeff Roberson	clockfreq = float(sys.argv[2])
1634ec5cae07SJeff Roberson
1635dcee3bd3SJeff Robersonroot = Tk()
1636932f0fa2SJeff Robersonroot.title("SchedGraph")
1637ec5cae07SJeff Robersoncolormap = Colormap(eventcolors)
1638ec5cae07SJeff Robersoncpucolormap = Colormap(cpucolors)
1639dcee3bd3SJeff Robersongraph = SchedGraph(root)
1640ec5cae07SJeff Robersonktrfile = KTRFile(sys.argv[1])
1641ec5cae07SJeff Robersongraph.draw()
1642dcee3bd3SJeff Robersonroot.mainloop()
1643