xref: /freebsd/tools/sched/schedgraph.py (revision 7e8ed296e120a57211d377102fa27dd9411d0fbd)
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# $FreeBSD$
28dcee3bd3SJeff Roberson
29749f65e3SCraig Rodriguesfrom __future__ import print_function
30dcee3bd3SJeff Robersonimport sys
31dcee3bd3SJeff Robersonimport re
32ec5cae07SJeff Robersonimport random
33*7e8ed296SAndriy Gaponfrom operator import attrgetter, itemgetter
34*7e8ed296SAndriy Gaponfrom functools import total_ordering
35*7e8ed296SAndriy Gaponfrom tkinter import *
36dcee3bd3SJeff Roberson
37698e6141SAndrew R. Reiter# To use:
3866835de4SSam Leffler# - Install the ports/x11-toolkits/py-tkinter package; e.g.
39*7e8ed296SAndriy Gapon#	pkg install x11-toolkits/py-tkinter
4066835de4SSam Leffler# - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF; e.g.
4166835de4SSam Leffler#	options 	KTR
4266835de4SSam Leffler#	options 	KTR_ENTRIES=32768
4366835de4SSam Leffler#	options 	KTR_COMPILE=(KTR_SCHED)
4466835de4SSam Leffler#	options 	KTR_MASK=(KTR_SCHED)
4566835de4SSam Leffler# - It is encouraged to increase KTR_ENTRIES size to gather enough
4666835de4SSam Leffler#    information for analysis; e.g.
4766835de4SSam Leffler#	options 	KTR_ENTRIES=262144
48e73e7730SKris Kennaway#   as 32768 entries may only correspond to a second or two of profiling
49e73e7730SKris Kennaway#   data depending on your workload.
5070015002SKris Kennaway# - Rebuild kernel with proper changes to KERNCONF and boot new kernel.
5170015002SKris Kennaway# - Run your workload to be profiled.
5270015002SKris Kennaway# - While the workload is continuing (i.e. before it finishes), disable
5370015002SKris Kennaway#   KTR tracing by setting 'sysctl debug.ktr.mask=0'.  This is necessary
5470015002SKris Kennaway#   to avoid a race condition while running ktrdump, i.e. the KTR ring buffer
5570015002SKris Kennaway#   will cycle a bit while ktrdump runs, and this confuses schedgraph because
5670015002SKris Kennaway#   the timestamps appear to go backwards at some point.  Stopping KTR logging
5770015002SKris Kennaway#   while the workload is still running is to avoid wasting log entries on
5870015002SKris Kennaway#   "idle" time at the end.
59698e6141SAndrew R. Reiter# - Dump the trace to a file: 'ktrdump -ct > ktr.out'
60*7e8ed296SAndriy Gapon# - Alternatively, use schedgraph.d script in this directory for getting
61*7e8ed296SAndriy Gapon#   the trace data by means of DTrace.  See the script for details.
62ec5cae07SJeff Roberson# - Run the python script: 'python schedgraph.py ktr.out' optionally provide
63ec5cae07SJeff Roberson#   your cpu frequency in ghz: 'python schedgraph.py ktr.out 2.4'
64698e6141SAndrew R. Reiter#
65698e6141SAndrew R. Reiter# To do:
66ec5cae07SJeff Roberson# Add a per-source summary display
67ec5cae07SJeff Roberson# "Vertical rule" to help relate data in different rows
68ec5cae07SJeff Roberson# Mouse-over popup of full thread/event/row label (currently truncated)
69ec5cae07SJeff Roberson# More visible anchors for popup event windows
7070015002SKris Kennaway#
7170015002SKris Kennaway# BUGS: 1) Only 8 CPUs are supported, more CPUs require more choices of
7270015002SKris Kennaway#          colours to represent them ;-)
73ec5cae07SJeff Roberson
74ec5cae07SJeff Robersoneventcolors = [
75ec5cae07SJeff Roberson	("count",	"red"),
76ec5cae07SJeff Roberson	("running",	"green"),
77ec5cae07SJeff Roberson	("idle",	"grey"),
782cba8dd3SJohn Baldwin	("spinning",	"red"),
79ec5cae07SJeff Roberson	("yielding",	"yellow"),
80ec5cae07SJeff Roberson	("swapped",	"violet"),
81ec5cae07SJeff Roberson	("suspended",	"purple"),
82ec5cae07SJeff Roberson	("iwait",	"grey"),
83ec5cae07SJeff Roberson	("sleep",	"blue"),
84ec5cae07SJeff Roberson	("blocked",	"dark red"),
85ec5cae07SJeff Roberson	("runq add",	"yellow"),
86ec5cae07SJeff Roberson	("runq rem",	"yellow"),
87ec5cae07SJeff Roberson	("thread exit",	"grey"),
88ec5cae07SJeff Roberson	("proc exit",	"grey"),
89ec5cae07SJeff Roberson	("lock acquire", "blue"),
90ec5cae07SJeff Roberson	("lock contest", "purple"),
91ec5cae07SJeff Roberson	("failed lock try", "red"),
92ec5cae07SJeff Roberson	("lock release", "grey"),
93932f0fa2SJeff Roberson	("statclock",	"black"),
94ec5cae07SJeff Roberson	("prio",	"black"),
95ec5cae07SJeff Roberson	("lend prio",	"black"),
96ec5cae07SJeff Roberson	("wokeup",	"black")
97ec5cae07SJeff Roberson]
98ec5cae07SJeff Roberson
99ec5cae07SJeff Robersoncpucolors = [
100ec5cae07SJeff Roberson	("CPU 0",	"light grey"),
101ec5cae07SJeff Roberson	("CPU 1",	"dark grey"),
102ec5cae07SJeff Roberson	("CPU 2",	"light blue"),
103ec5cae07SJeff Roberson	("CPU 3",	"light pink"),
104ec5cae07SJeff Roberson	("CPU 4",	"blanched almond"),
105ec5cae07SJeff Roberson	("CPU 5",	"slate grey"),
106ec5cae07SJeff Roberson	("CPU 6",	"tan"),
107ec5cae07SJeff Roberson	("CPU 7",	"thistle"),
108ec5cae07SJeff Roberson	("CPU 8",	"white")
109ec5cae07SJeff Roberson]
110ec5cae07SJeff Roberson
111ec5cae07SJeff Robersoncolors = [
112ec5cae07SJeff Roberson	"white", "thistle", "blanched almond", "tan", "chartreuse",
113ec5cae07SJeff Roberson	"dark red", "red", "pale violet red", "pink", "light pink",
114ec5cae07SJeff Roberson	"dark orange", "orange", "coral", "light coral",
115ec5cae07SJeff Roberson	"goldenrod", "gold", "yellow", "light yellow",
116ec5cae07SJeff Roberson	"dark green", "green", "light green", "light sea green",
117ec5cae07SJeff Roberson	"dark blue", "blue", "light blue", "steel blue", "light slate blue",
118ec5cae07SJeff Roberson	"dark violet", "violet", "purple", "blue violet",
119ec5cae07SJeff Roberson	"dark grey", "slate grey", "light grey",
120ec5cae07SJeff Roberson	"black",
121ec5cae07SJeff Roberson]
122ec5cae07SJeff Robersoncolors.sort()
123dcee3bd3SJeff Roberson
124dcee3bd3SJeff Robersonticksps = None
125dcee3bd3SJeff Robersonstatus = None
126ec5cae07SJeff Robersoncolormap = None
127ec5cae07SJeff Robersonktrfile = None
128ec5cae07SJeff Robersonclockfreq = None
129ec5cae07SJeff Robersonsources = []
13066835de4SSam Lefflerlineno = -1
131dcee3bd3SJeff Roberson
132932f0fa2SJeff RobersonY_BORDER = 10
133932f0fa2SJeff RobersonX_BORDER = 10
134932f0fa2SJeff RobersonY_COUNTER = 80
135932f0fa2SJeff RobersonY_EVENTSOURCE = 10
136932f0fa2SJeff RobersonXY_POINT = 4
137932f0fa2SJeff Roberson
138ec5cae07SJeff Robersonclass Colormap:
139ec5cae07SJeff Roberson	def __init__(self, table):
140ec5cae07SJeff Roberson		self.table = table
141ec5cae07SJeff Roberson		self.map = {}
142ec5cae07SJeff Roberson		for entry in table:
143ec5cae07SJeff Roberson			self.map[entry[0]] = entry[1]
144ec5cae07SJeff Roberson
145ec5cae07SJeff Roberson	def lookup(self, name):
146ec5cae07SJeff Roberson		try:
147ec5cae07SJeff Roberson			color = self.map[name]
148ec5cae07SJeff Roberson		except:
149ec5cae07SJeff Roberson			color = colors[random.randrange(0, len(colors))]
150749f65e3SCraig Rodrigues			print("Picking random color", color, "for", name)
151ec5cae07SJeff Roberson			self.map[name] = color
152ec5cae07SJeff Roberson			self.table.append((name, color))
153ec5cae07SJeff Roberson		return (color)
154ec5cae07SJeff Roberson
155dcee3bd3SJeff Robersondef ticks2sec(ticks):
156b62baf95SJeff Roberson	ticks = float(ticks)
157b62baf95SJeff Roberson	ns = float(ticksps) / 1000000000
158b62baf95SJeff Roberson	ticks /= ns
159dcee3bd3SJeff Roberson	if (ticks < 1000):
160b62baf95SJeff Roberson		return ("%.2fns" % ticks)
161dcee3bd3SJeff Roberson	ticks /= 1000
162dcee3bd3SJeff Roberson	if (ticks < 1000):
163b62baf95SJeff Roberson		return ("%.2fus" % ticks)
164dcee3bd3SJeff Roberson	ticks /= 1000
165b62baf95SJeff Roberson	if (ticks < 1000):
166b62baf95SJeff Roberson		return ("%.2fms" % ticks)
167b62baf95SJeff Roberson	ticks /= 1000
168b62baf95SJeff Roberson	return ("%.2fs" % ticks)
169dcee3bd3SJeff Roberson
170dcee3bd3SJeff Robersonclass Scaler(Frame):
171dcee3bd3SJeff Roberson	def __init__(self, master, target):
172dcee3bd3SJeff Roberson		Frame.__init__(self, master)
17316ef0f3bSJeff Roberson		self.scale = None
17416ef0f3bSJeff Roberson		self.target = target
175dcee3bd3SJeff Roberson		self.label = Label(self, text="Ticks per pixel")
176dcee3bd3SJeff Roberson		self.label.pack(side=LEFT)
17716ef0f3bSJeff Roberson		self.resolution = 100
17816ef0f3bSJeff Roberson		self.setmax(10000)
179dcee3bd3SJeff Roberson
180dcee3bd3SJeff Roberson	def scaleset(self, value):
181dcee3bd3SJeff Roberson		self.target.scaleset(int(value))
182dcee3bd3SJeff Roberson
183dcee3bd3SJeff Roberson	def set(self, value):
184dcee3bd3SJeff Roberson		self.scale.set(value)
185dcee3bd3SJeff Roberson
18616ef0f3bSJeff Roberson	def setmax(self, value):
18716ef0f3bSJeff Roberson		#
18816ef0f3bSJeff Roberson		# We can't reconfigure the to_ value so we delete the old
18916ef0f3bSJeff Roberson		# window and make a new one when we resize.
19016ef0f3bSJeff Roberson		#
19116ef0f3bSJeff Roberson		if (self.scale != None):
19216ef0f3bSJeff Roberson			self.scale.pack_forget()
19316ef0f3bSJeff Roberson			self.scale.destroy()
19416ef0f3bSJeff Roberson		self.scale = Scale(self, command=self.scaleset,
19516ef0f3bSJeff Roberson		    from_=100, to_=value, orient=HORIZONTAL,
19616ef0f3bSJeff Roberson		    resolution=self.resolution)
19716ef0f3bSJeff Roberson		self.scale.pack(fill="both", expand=1)
19816ef0f3bSJeff Roberson		self.scale.set(self.target.scaleget())
19916ef0f3bSJeff Roberson
200dcee3bd3SJeff Robersonclass Status(Frame):
201dcee3bd3SJeff Roberson	def __init__(self, master):
202dcee3bd3SJeff Roberson		Frame.__init__(self, master)
203dcee3bd3SJeff Roberson		self.label = Label(self, bd=1, relief=SUNKEN, anchor=W)
204dcee3bd3SJeff Roberson		self.label.pack(fill="both", expand=1)
205dcee3bd3SJeff Roberson		self.clear()
206dcee3bd3SJeff Roberson
207dcee3bd3SJeff Roberson	def set(self, str):
208dcee3bd3SJeff Roberson		self.label.config(text=str)
209dcee3bd3SJeff Roberson
210dcee3bd3SJeff Roberson	def clear(self):
211dcee3bd3SJeff Roberson		self.label.config(text="")
212dcee3bd3SJeff Roberson
213dcee3bd3SJeff Roberson	def startup(self, str):
214dcee3bd3SJeff Roberson		self.set(str)
215dcee3bd3SJeff Roberson		root.update()
216dcee3bd3SJeff Roberson
217ec5cae07SJeff Robersonclass ColorConf(Frame):
218ec5cae07SJeff Roberson	def __init__(self, master, name, color):
219dcee3bd3SJeff Roberson		Frame.__init__(self, master)
220ec5cae07SJeff Roberson		if (graph.getstate(name) == "hidden"):
221ec5cae07SJeff Roberson			enabled = 0
222ec5cae07SJeff Roberson		else:
223ec5cae07SJeff Roberson			enabled = 1
224dcee3bd3SJeff Roberson		self.name = name
225dcee3bd3SJeff Roberson		self.color = StringVar()
226dcee3bd3SJeff Roberson		self.color_default = color
227dcee3bd3SJeff Roberson		self.color_current = color
228dcee3bd3SJeff Roberson		self.color.set(color)
229dcee3bd3SJeff Roberson		self.enabled = IntVar()
230dcee3bd3SJeff Roberson		self.enabled_default = enabled
231dcee3bd3SJeff Roberson		self.enabled_current = enabled
232dcee3bd3SJeff Roberson		self.enabled.set(enabled)
233dcee3bd3SJeff Roberson		self.draw()
234dcee3bd3SJeff Roberson
235dcee3bd3SJeff Roberson	def draw(self):
236dcee3bd3SJeff Roberson		self.label = Label(self, text=self.name, anchor=W)
237dcee3bd3SJeff Roberson		self.sample = Canvas(self, width=24, height=24,
238dcee3bd3SJeff Roberson		    bg='grey')
239dcee3bd3SJeff Roberson		self.rect = self.sample.create_rectangle(0, 0, 24, 24,
240dcee3bd3SJeff Roberson		    fill=self.color.get())
241ec5cae07SJeff Roberson		self.list = OptionMenu(self, self.color, command=self.setcolor,
242ec5cae07SJeff Roberson		    *colors)
243dcee3bd3SJeff Roberson		self.checkbox = Checkbutton(self, text="enabled",
244dcee3bd3SJeff Roberson		    variable=self.enabled)
245dcee3bd3SJeff Roberson		self.label.grid(row=0, column=0, sticky=E+W)
246dcee3bd3SJeff Roberson		self.sample.grid(row=0, column=1)
247dcee3bd3SJeff Roberson		self.list.grid(row=0, column=2, sticky=E+W)
248dcee3bd3SJeff Roberson		self.checkbox.grid(row=0, column=3)
249dcee3bd3SJeff Roberson		self.columnconfigure(0, weight=1)
250ec5cae07SJeff Roberson		self.columnconfigure(2, minsize=150)
251dcee3bd3SJeff Roberson
252dcee3bd3SJeff Roberson	def setcolor(self, color):
253dcee3bd3SJeff Roberson		self.color.set(color)
254dcee3bd3SJeff Roberson		self.sample.itemconfigure(self.rect, fill=color)
255dcee3bd3SJeff Roberson
256dcee3bd3SJeff Roberson	def apply(self):
257dcee3bd3SJeff Roberson		cchange = 0
258dcee3bd3SJeff Roberson		echange = 0
259dcee3bd3SJeff Roberson		if (self.color_current != self.color.get()):
260dcee3bd3SJeff Roberson			cchange = 1
261dcee3bd3SJeff Roberson		if (self.enabled_current != self.enabled.get()):
262dcee3bd3SJeff Roberson			echange = 1
263dcee3bd3SJeff Roberson		self.color_current = self.color.get()
264dcee3bd3SJeff Roberson		self.enabled_current = self.enabled.get()
265dcee3bd3SJeff Roberson		if (echange != 0):
266dcee3bd3SJeff Roberson			if (self.enabled_current):
267dcee3bd3SJeff Roberson				graph.setcolor(self.name, self.color_current)
268dcee3bd3SJeff Roberson			else:
269dcee3bd3SJeff Roberson				graph.hide(self.name)
270dcee3bd3SJeff Roberson			return
271dcee3bd3SJeff Roberson		if (cchange != 0):
272dcee3bd3SJeff Roberson			graph.setcolor(self.name, self.color_current)
273dcee3bd3SJeff Roberson
274dcee3bd3SJeff Roberson	def revert(self):
275dcee3bd3SJeff Roberson		self.setcolor(self.color_default)
276dcee3bd3SJeff Roberson		self.enabled.set(self.enabled_default)
277dcee3bd3SJeff Roberson
278ec5cae07SJeff Robersonclass ColorConfigure(Toplevel):
279ec5cae07SJeff Roberson	def __init__(self, table, name):
280dcee3bd3SJeff Roberson		Toplevel.__init__(self)
281dcee3bd3SJeff Roberson		self.resizable(0, 0)
282ec5cae07SJeff Roberson		self.title(name)
283ec5cae07SJeff Roberson		self.items = LabelFrame(self, text="Item Type")
284dcee3bd3SJeff Roberson		self.buttons = Frame(self)
285dcee3bd3SJeff Roberson		self.drawbuttons()
286dcee3bd3SJeff Roberson		self.items.grid(row=0, column=0, sticky=E+W)
287dcee3bd3SJeff Roberson		self.columnconfigure(0, weight=1)
288dcee3bd3SJeff Roberson		self.buttons.grid(row=1, column=0, sticky=E+W)
289dcee3bd3SJeff Roberson		self.types = []
290dcee3bd3SJeff Roberson		self.irow = 0
291ec5cae07SJeff Roberson		for type in table:
292ec5cae07SJeff Roberson			color = graph.getcolor(type[0])
293ec5cae07SJeff Roberson			if (color != ""):
294ec5cae07SJeff Roberson				self.additem(type[0], color)
2959799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
2969799411bSJohn Baldwin
2979799411bSJohn Baldwin	def destroycb(self, event):
2989799411bSJohn Baldwin		self.destroy()
299dcee3bd3SJeff Roberson
300ec5cae07SJeff Roberson	def additem(self, name, color):
301ec5cae07SJeff Roberson		item = ColorConf(self.items, name, color)
302dcee3bd3SJeff Roberson		self.types.append(item)
303dcee3bd3SJeff Roberson		item.grid(row=self.irow, column=0, sticky=E+W)
304dcee3bd3SJeff Roberson		self.irow += 1
305dcee3bd3SJeff Roberson
306dcee3bd3SJeff Roberson	def drawbuttons(self):
307dcee3bd3SJeff Roberson		self.apply = Button(self.buttons, text="Apply",
308dcee3bd3SJeff Roberson		    command=self.apress)
309ec5cae07SJeff Roberson		self.default = Button(self.buttons, text="Revert",
310dcee3bd3SJeff Roberson		    command=self.rpress)
311dcee3bd3SJeff Roberson		self.apply.grid(row=0, column=0, sticky=E+W)
312ec5cae07SJeff Roberson		self.default.grid(row=0, column=1, sticky=E+W)
313dcee3bd3SJeff Roberson		self.buttons.columnconfigure(0, weight=1)
314dcee3bd3SJeff Roberson		self.buttons.columnconfigure(1, weight=1)
315dcee3bd3SJeff Roberson
316dcee3bd3SJeff Roberson	def apress(self):
317dcee3bd3SJeff Roberson		for item in self.types:
318dcee3bd3SJeff Roberson			item.apply()
319dcee3bd3SJeff Roberson
320dcee3bd3SJeff Roberson	def rpress(self):
321dcee3bd3SJeff Roberson		for item in self.types:
322dcee3bd3SJeff Roberson			item.revert()
323dcee3bd3SJeff Roberson
3243d21f0f4SJeff Robersonclass SourceConf(Frame):
3253d21f0f4SJeff Roberson	def __init__(self, master, source):
3263d21f0f4SJeff Roberson		Frame.__init__(self, master)
3273d21f0f4SJeff Roberson		if (source.hidden == 1):
3283d21f0f4SJeff Roberson			enabled = 0
3293d21f0f4SJeff Roberson		else:
3303d21f0f4SJeff Roberson			enabled = 1
3313d21f0f4SJeff Roberson		self.source = source
3323d21f0f4SJeff Roberson		self.name = source.name
3333d21f0f4SJeff Roberson		self.enabled = IntVar()
3343d21f0f4SJeff Roberson		self.enabled_default = enabled
3353d21f0f4SJeff Roberson		self.enabled_current = enabled
3363d21f0f4SJeff Roberson		self.enabled.set(enabled)
3373d21f0f4SJeff Roberson		self.draw()
3383d21f0f4SJeff Roberson
3393d21f0f4SJeff Roberson	def draw(self):
3403d21f0f4SJeff Roberson		self.label = Label(self, text=self.name, anchor=W)
3413d21f0f4SJeff Roberson		self.checkbox = Checkbutton(self, text="enabled",
3423d21f0f4SJeff Roberson		    variable=self.enabled)
3433d21f0f4SJeff Roberson		self.label.grid(row=0, column=0, sticky=E+W)
3443d21f0f4SJeff Roberson		self.checkbox.grid(row=0, column=1)
3453d21f0f4SJeff Roberson		self.columnconfigure(0, weight=1)
3463d21f0f4SJeff Roberson
34750d670daSJeff Roberson	def changed(self):
3483d21f0f4SJeff Roberson		if (self.enabled_current != self.enabled.get()):
34950d670daSJeff Roberson			return 1
35050d670daSJeff Roberson		return 0
35150d670daSJeff Roberson
35250d670daSJeff Roberson	def apply(self):
3533d21f0f4SJeff Roberson		self.enabled_current = self.enabled.get()
3543d21f0f4SJeff Roberson
3553d21f0f4SJeff Roberson	def revert(self):
3563d21f0f4SJeff Roberson		self.enabled.set(self.enabled_default)
3573d21f0f4SJeff Roberson
3583d21f0f4SJeff Roberson	def check(self):
3593d21f0f4SJeff Roberson		self.enabled.set(1)
3603d21f0f4SJeff Roberson
3613d21f0f4SJeff Roberson	def uncheck(self):
3623d21f0f4SJeff Roberson		self.enabled.set(0)
3633d21f0f4SJeff Roberson
3643d21f0f4SJeff Robersonclass SourceConfigure(Toplevel):
3653d21f0f4SJeff Roberson	def __init__(self):
3663d21f0f4SJeff Roberson		Toplevel.__init__(self)
3673d21f0f4SJeff Roberson		self.resizable(0, 0)
3683d21f0f4SJeff Roberson		self.title("Source Configuration")
3693d21f0f4SJeff Roberson		self.items = []
3703d21f0f4SJeff Roberson		self.iframe = Frame(self)
3713d21f0f4SJeff Roberson		self.iframe.grid(row=0, column=0, sticky=E+W)
3723d21f0f4SJeff Roberson		f = LabelFrame(self.iframe, bd=4, text="Sources")
3733d21f0f4SJeff Roberson		self.items.append(f)
3743d21f0f4SJeff Roberson		self.buttons = Frame(self)
3753d21f0f4SJeff Roberson		self.items[0].grid(row=0, column=0, sticky=E+W)
3763d21f0f4SJeff Roberson		self.columnconfigure(0, weight=1)
3773d21f0f4SJeff Roberson		self.sconfig = []
3783d21f0f4SJeff Roberson		self.irow = 0
3793d21f0f4SJeff Roberson		self.icol = 0
3803d21f0f4SJeff Roberson		for source in sources:
3813d21f0f4SJeff Roberson			self.addsource(source)
3823d21f0f4SJeff Roberson		self.drawbuttons()
3833d21f0f4SJeff Roberson		self.buttons.grid(row=1, column=0, sticky=W)
3849799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
3859799411bSJohn Baldwin
3869799411bSJohn Baldwin	def destroycb(self, event):
3879799411bSJohn Baldwin		self.destroy()
3883d21f0f4SJeff Roberson
3893d21f0f4SJeff Roberson	def addsource(self, source):
3903d21f0f4SJeff Roberson		if (self.irow > 30):
3913d21f0f4SJeff Roberson			self.icol += 1
3923d21f0f4SJeff Roberson			self.irow = 0
3933d21f0f4SJeff Roberson			c = self.icol
3943d21f0f4SJeff Roberson			f = LabelFrame(self.iframe, bd=4, text="Sources")
3953d21f0f4SJeff Roberson			f.grid(row=0, column=c, sticky=N+E+W)
3963d21f0f4SJeff Roberson			self.items.append(f)
3973d21f0f4SJeff Roberson		item = SourceConf(self.items[self.icol], source)
3983d21f0f4SJeff Roberson		self.sconfig.append(item)
3993d21f0f4SJeff Roberson		item.grid(row=self.irow, column=0, sticky=E+W)
4003d21f0f4SJeff Roberson		self.irow += 1
4013d21f0f4SJeff Roberson
4023d21f0f4SJeff Roberson	def drawbuttons(self):
4033d21f0f4SJeff Roberson		self.apply = Button(self.buttons, text="Apply",
4043d21f0f4SJeff Roberson		    command=self.apress)
4053d21f0f4SJeff Roberson		self.default = Button(self.buttons, text="Revert",
4063d21f0f4SJeff Roberson		    command=self.rpress)
4073d21f0f4SJeff Roberson		self.checkall = Button(self.buttons, text="Check All",
4083d21f0f4SJeff Roberson		    command=self.cpress)
4093d21f0f4SJeff Roberson		self.uncheckall = Button(self.buttons, text="Uncheck All",
4103d21f0f4SJeff Roberson		    command=self.upress)
4113d21f0f4SJeff Roberson		self.checkall.grid(row=0, column=0, sticky=W)
4123d21f0f4SJeff Roberson		self.uncheckall.grid(row=0, column=1, sticky=W)
4133d21f0f4SJeff Roberson		self.apply.grid(row=0, column=2, sticky=W)
4143d21f0f4SJeff Roberson		self.default.grid(row=0, column=3, sticky=W)
4153d21f0f4SJeff Roberson		self.buttons.columnconfigure(0, weight=1)
4163d21f0f4SJeff Roberson		self.buttons.columnconfigure(1, weight=1)
4173d21f0f4SJeff Roberson		self.buttons.columnconfigure(2, weight=1)
4183d21f0f4SJeff Roberson		self.buttons.columnconfigure(3, weight=1)
4193d21f0f4SJeff Roberson
4203d21f0f4SJeff Roberson	def apress(self):
42150d670daSJeff Roberson		disable_sources = []
42250d670daSJeff Roberson		enable_sources = []
42350d670daSJeff Roberson		for item in self.sconfig:
42450d670daSJeff Roberson			if (item.changed() == 0):
42550d670daSJeff Roberson				continue
42650d670daSJeff Roberson			if (item.enabled.get() == 1):
42750d670daSJeff Roberson				enable_sources.append(item.source)
42850d670daSJeff Roberson			else:
42950d670daSJeff Roberson				disable_sources.append(item.source)
43050d670daSJeff Roberson
43150d670daSJeff Roberson		if (len(disable_sources)):
43250d670daSJeff Roberson			graph.sourcehidelist(disable_sources)
43350d670daSJeff Roberson		if (len(enable_sources)):
43450d670daSJeff Roberson			graph.sourceshowlist(enable_sources)
43550d670daSJeff Roberson
4363d21f0f4SJeff Roberson		for item in self.sconfig:
4373d21f0f4SJeff Roberson			item.apply()
4383d21f0f4SJeff Roberson
4393d21f0f4SJeff Roberson	def rpress(self):
4403d21f0f4SJeff Roberson		for item in self.sconfig:
4413d21f0f4SJeff Roberson			item.revert()
4423d21f0f4SJeff Roberson
4433d21f0f4SJeff Roberson	def cpress(self):
4443d21f0f4SJeff Roberson		for item in self.sconfig:
4453d21f0f4SJeff Roberson			item.check()
4463d21f0f4SJeff Roberson
4473d21f0f4SJeff Roberson	def upress(self):
4483d21f0f4SJeff Roberson		for item in self.sconfig:
4493d21f0f4SJeff Roberson			item.uncheck()
4503d21f0f4SJeff Roberson
45150d670daSJeff Robersonclass SourceStats(Toplevel):
45250d670daSJeff Roberson	def __init__(self, source):
45350d670daSJeff Roberson		self.source = source
45450d670daSJeff Roberson		Toplevel.__init__(self)
45550d670daSJeff Roberson		self.resizable(0, 0)
45650d670daSJeff Roberson		self.title(source.name + " statistics")
45750d670daSJeff Roberson		self.evframe = LabelFrame(self,
458b62baf95SJeff Roberson		    text="Event Count, Duration, Avg Duration")
45950d670daSJeff Roberson		self.evframe.grid(row=0, column=0, sticky=E+W)
46050d670daSJeff Roberson		eventtypes={}
46150d670daSJeff Roberson		for event in self.source.events:
46250d670daSJeff Roberson			if (event.type == "pad"):
46350d670daSJeff Roberson				continue
46450d670daSJeff Roberson			duration = event.duration
465aef675d8SCraig Rodrigues			if (event.name in eventtypes):
46650d670daSJeff Roberson				(c, d) = eventtypes[event.name]
46750d670daSJeff Roberson				c += 1
46850d670daSJeff Roberson				d += duration
46950d670daSJeff Roberson				eventtypes[event.name] = (c, d)
47050d670daSJeff Roberson			else:
47150d670daSJeff Roberson				eventtypes[event.name] = (1, duration)
47250d670daSJeff Roberson		events = []
47350d670daSJeff Roberson		for k, v in eventtypes.iteritems():
47450d670daSJeff Roberson			(c, d) = v
47550d670daSJeff Roberson			events.append((k, c, d))
476*7e8ed296SAndriy Gapon		events.sort(key=itemgetter(1), reverse=True)
47750d670daSJeff Roberson
47850d670daSJeff Roberson		ypos = 0
47950d670daSJeff Roberson		for event in events:
48050d670daSJeff Roberson			(name, c, d) = event
481b62baf95SJeff Roberson			Label(self.evframe, text=name, bd=1,
482b62baf95SJeff Roberson			    relief=SUNKEN, anchor=W, width=30).grid(
483b62baf95SJeff Roberson			    row=ypos, column=0, sticky=W+E)
484b62baf95SJeff Roberson			Label(self.evframe, text=str(c), bd=1,
485b62baf95SJeff Roberson			    relief=SUNKEN, anchor=W, width=10).grid(
486b62baf95SJeff Roberson			    row=ypos, column=1, sticky=W+E)
487b62baf95SJeff Roberson			Label(self.evframe, text=ticks2sec(d),
488b62baf95SJeff Roberson			    bd=1, relief=SUNKEN, width=10).grid(
489b62baf95SJeff Roberson			    row=ypos, column=2, sticky=W+E)
490b62baf95SJeff Roberson			if (d and c):
491b62baf95SJeff Roberson				d /= c
492b62baf95SJeff Roberson			else:
493b62baf95SJeff Roberson				d = 0
494b62baf95SJeff Roberson			Label(self.evframe, text=ticks2sec(d),
495b62baf95SJeff Roberson			    bd=1, relief=SUNKEN, width=10).grid(
496b62baf95SJeff Roberson			    row=ypos, column=3, sticky=W+E)
49750d670daSJeff Roberson			ypos += 1
4989799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
4999799411bSJohn Baldwin
5009799411bSJohn Baldwin	def destroycb(self, event):
5019799411bSJohn Baldwin		self.destroy()
50250d670daSJeff Roberson
50350d670daSJeff Roberson
50450d670daSJeff Robersonclass SourceContext(Menu):
50550d670daSJeff Roberson	def __init__(self, event, source):
50650d670daSJeff Roberson		self.source = source
50750d670daSJeff Roberson		Menu.__init__(self, tearoff=0, takefocus=0)
50850d670daSJeff Roberson		self.add_command(label="hide", command=self.hide)
50950d670daSJeff Roberson		self.add_command(label="hide group", command=self.hidegroup)
51050d670daSJeff Roberson		self.add_command(label="stats", command=self.stats)
51150d670daSJeff Roberson		self.tk_popup(event.x_root-3, event.y_root+3)
51250d670daSJeff Roberson
51350d670daSJeff Roberson	def hide(self):
51450d670daSJeff Roberson		graph.sourcehide(self.source)
51550d670daSJeff Roberson
51650d670daSJeff Roberson	def hidegroup(self):
51750d670daSJeff Roberson		grouplist = []
51850d670daSJeff Roberson		for source in sources:
51950d670daSJeff Roberson			if (source.group == self.source.group):
52050d670daSJeff Roberson				grouplist.append(source)
52150d670daSJeff Roberson		graph.sourcehidelist(grouplist)
52250d670daSJeff Roberson
52350d670daSJeff Roberson	def show(self):
52450d670daSJeff Roberson		graph.sourceshow(self.source)
52550d670daSJeff Roberson
52650d670daSJeff Roberson	def stats(self):
52750d670daSJeff Roberson		SourceStats(self.source)
52850d670daSJeff Roberson
529dcee3bd3SJeff Robersonclass EventView(Toplevel):
530dcee3bd3SJeff Roberson	def __init__(self, event, canvas):
531dcee3bd3SJeff Roberson		Toplevel.__init__(self)
532dcee3bd3SJeff Roberson		self.resizable(0, 0)
533dcee3bd3SJeff Roberson		self.title("Event")
534dcee3bd3SJeff Roberson		self.event = event
535dcee3bd3SJeff Roberson		self.buttons = Frame(self)
536ec5cae07SJeff Roberson		self.buttons.grid(row=0, column=0, sticky=E+W)
537ec5cae07SJeff Roberson		self.frame = Frame(self)
538ec5cae07SJeff Roberson		self.frame.grid(row=1, column=0, sticky=N+S+E+W)
539dcee3bd3SJeff Roberson		self.canvas = canvas
540dcee3bd3SJeff Roberson		self.drawlabels()
541dcee3bd3SJeff Roberson		self.drawbuttons()
542dcee3bd3SJeff Roberson		event.displayref(canvas)
543dcee3bd3SJeff Roberson		self.bind("<Destroy>", self.destroycb)
5449799411bSJohn Baldwin		self.bind("<Control-w>", self.destroycb)
545dcee3bd3SJeff Roberson
546dcee3bd3SJeff Roberson	def destroycb(self, event):
547dcee3bd3SJeff Roberson		self.unbind("<Destroy>")
548dcee3bd3SJeff Roberson		if (self.event != None):
549dcee3bd3SJeff Roberson			self.event.displayunref(self.canvas)
550dcee3bd3SJeff Roberson			self.event = None
551dcee3bd3SJeff Roberson		self.destroy()
552dcee3bd3SJeff Roberson
553dcee3bd3SJeff Roberson	def clearlabels(self):
554dcee3bd3SJeff Roberson		for label in self.frame.grid_slaves():
555dcee3bd3SJeff Roberson			label.grid_remove()
556dcee3bd3SJeff Roberson
557dcee3bd3SJeff Roberson	def drawlabels(self):
558dcee3bd3SJeff Roberson		ypos = 0
559dcee3bd3SJeff Roberson		labels = self.event.labels()
560dcee3bd3SJeff Roberson		while (len(labels) < 7):
561ec5cae07SJeff Roberson			labels.append(("", ""))
562dcee3bd3SJeff Roberson		for label in labels:
563ec5cae07SJeff Roberson			name, value = label
564ec5cae07SJeff Roberson			linked = 0
565ec5cae07SJeff Roberson			if (name == "linkedto"):
566ec5cae07SJeff Roberson				linked = 1
567dcee3bd3SJeff Roberson			l = Label(self.frame, text=name, bd=1, width=15,
568dcee3bd3SJeff Roberson			    relief=SUNKEN, anchor=W)
569dcee3bd3SJeff Roberson			if (linked):
570dcee3bd3SJeff Roberson				fgcolor = "blue"
571dcee3bd3SJeff Roberson			else:
572dcee3bd3SJeff Roberson				fgcolor = "black"
573dcee3bd3SJeff Roberson			r = Label(self.frame, text=value, bd=1,
574dcee3bd3SJeff Roberson			    relief=SUNKEN, anchor=W, fg=fgcolor)
575dcee3bd3SJeff Roberson			l.grid(row=ypos, column=0, sticky=E+W)
576dcee3bd3SJeff Roberson			r.grid(row=ypos, column=1, sticky=E+W)
577dcee3bd3SJeff Roberson			if (linked):
578dcee3bd3SJeff Roberson				r.bind("<Button-1>", self.linkpress)
579dcee3bd3SJeff Roberson			ypos += 1
580dcee3bd3SJeff Roberson		self.frame.columnconfigure(1, minsize=80)
581dcee3bd3SJeff Roberson
582dcee3bd3SJeff Roberson	def drawbuttons(self):
583dcee3bd3SJeff Roberson		self.back = Button(self.buttons, text="<", command=self.bpress)
584dcee3bd3SJeff Roberson		self.forw = Button(self.buttons, text=">", command=self.fpress)
585dcee3bd3SJeff Roberson		self.new = Button(self.buttons, text="new", command=self.npress)
586dcee3bd3SJeff Roberson		self.back.grid(row=0, column=0, sticky=E+W)
587dcee3bd3SJeff Roberson		self.forw.grid(row=0, column=1, sticky=E+W)
588dcee3bd3SJeff Roberson		self.new.grid(row=0, column=2, sticky=E+W)
589dcee3bd3SJeff Roberson		self.buttons.columnconfigure(2, weight=1)
590dcee3bd3SJeff Roberson
591dcee3bd3SJeff Roberson	def newevent(self, event):
592dcee3bd3SJeff Roberson		self.event.displayunref(self.canvas)
593dcee3bd3SJeff Roberson		self.clearlabels()
594dcee3bd3SJeff Roberson		self.event = event
595dcee3bd3SJeff Roberson		self.event.displayref(self.canvas)
596dcee3bd3SJeff Roberson		self.drawlabels()
597dcee3bd3SJeff Roberson
598dcee3bd3SJeff Roberson	def npress(self):
599dcee3bd3SJeff Roberson		EventView(self.event, self.canvas)
600dcee3bd3SJeff Roberson
601dcee3bd3SJeff Roberson	def bpress(self):
602dcee3bd3SJeff Roberson		prev = self.event.prev()
603dcee3bd3SJeff Roberson		if (prev == None):
604dcee3bd3SJeff Roberson			return
605ec5cae07SJeff Roberson		while (prev.type == "pad"):
606dcee3bd3SJeff Roberson			prev = prev.prev()
607dcee3bd3SJeff Roberson			if (prev == None):
608dcee3bd3SJeff Roberson				return
609dcee3bd3SJeff Roberson		self.newevent(prev)
610dcee3bd3SJeff Roberson
611dcee3bd3SJeff Roberson	def fpress(self):
612dcee3bd3SJeff Roberson		next = self.event.next()
613dcee3bd3SJeff Roberson		if (next == None):
614dcee3bd3SJeff Roberson			return
615ec5cae07SJeff Roberson		while (next.type == "pad"):
616dcee3bd3SJeff Roberson			next = next.next()
617dcee3bd3SJeff Roberson			if (next == None):
618dcee3bd3SJeff Roberson				return
619dcee3bd3SJeff Roberson		self.newevent(next)
620dcee3bd3SJeff Roberson
621dcee3bd3SJeff Roberson	def linkpress(self, wevent):
622dcee3bd3SJeff Roberson		event = self.event.getlinked()
623dcee3bd3SJeff Roberson		if (event != None):
624dcee3bd3SJeff Roberson			self.newevent(event)
625dcee3bd3SJeff Roberson
626dcee3bd3SJeff Robersonclass Event:
627ec5cae07SJeff Roberson	def __init__(self, source, name, cpu, timestamp, attrs):
628dcee3bd3SJeff Roberson		self.source = source
629ec5cae07SJeff Roberson		self.name = name
630dcee3bd3SJeff Roberson		self.cpu = cpu
631dcee3bd3SJeff Roberson		self.timestamp = int(timestamp)
632ec5cae07SJeff Roberson		self.attrs = attrs
633dcee3bd3SJeff Roberson		self.idx = None
634dcee3bd3SJeff Roberson		self.item = None
635dcee3bd3SJeff Roberson		self.dispcnt = 0
63650d670daSJeff Roberson		self.duration = 0
63766835de4SSam Leffler		self.recno = lineno
638dcee3bd3SJeff Roberson
639dcee3bd3SJeff Roberson	def status(self):
640dcee3bd3SJeff Roberson		statstr = self.name + " " + self.source.name
641dcee3bd3SJeff Roberson		statstr += " on: cpu" + str(self.cpu)
642dcee3bd3SJeff Roberson		statstr += " at: " + str(self.timestamp)
643ec5cae07SJeff Roberson		statstr += " attributes: "
644ec5cae07SJeff Roberson		for i in range(0, len(self.attrs)):
645ec5cae07SJeff Roberson			attr = self.attrs[i]
646ec5cae07SJeff Roberson			statstr += attr[0] + ": " + str(attr[1])
647ec5cae07SJeff Roberson			if (i != len(self.attrs) - 1):
648ec5cae07SJeff Roberson				statstr += ", "
649dcee3bd3SJeff Roberson		status.set(statstr)
650dcee3bd3SJeff Roberson
651dcee3bd3SJeff Roberson	def labels(self):
652ec5cae07SJeff Roberson		return [("Source", self.source.name),
653ec5cae07SJeff Roberson			("Event", self.name),
654ec5cae07SJeff Roberson			("CPU", self.cpu),
655ec5cae07SJeff Roberson			("Timestamp", self.timestamp),
656ec5cae07SJeff Roberson			("KTR Line ", self.recno)
657ec5cae07SJeff Roberson		] + self.attrs
658ec5cae07SJeff Roberson
659ec5cae07SJeff Roberson	def mouseenter(self, canvas):
660dcee3bd3SJeff Roberson		self.displayref(canvas)
661dcee3bd3SJeff Roberson		self.status()
662dcee3bd3SJeff Roberson
663ec5cae07SJeff Roberson	def mouseexit(self, canvas):
664dcee3bd3SJeff Roberson		self.displayunref(canvas)
665dcee3bd3SJeff Roberson		status.clear()
666dcee3bd3SJeff Roberson
667ec5cae07SJeff Roberson	def mousepress(self, canvas):
668dcee3bd3SJeff Roberson		EventView(self, canvas)
669dcee3bd3SJeff Roberson
670ec5cae07SJeff Roberson	def draw(self, canvas, xpos, ypos, item):
671ec5cae07SJeff Roberson		self.item = item
672ec5cae07SJeff Roberson		if (item != None):
673ec5cae07SJeff Roberson			canvas.items[item] = self
674ec5cae07SJeff Roberson
675ec5cae07SJeff Roberson	def move(self, canvas, x, y):
676ec5cae07SJeff Roberson		if (self.item == None):
677ec5cae07SJeff Roberson			return;
678ec5cae07SJeff Roberson		canvas.move(self.item, x, y);
679ec5cae07SJeff Roberson
680dcee3bd3SJeff Roberson	def next(self):
681dcee3bd3SJeff Roberson		return self.source.eventat(self.idx + 1)
682dcee3bd3SJeff Roberson
683ec5cae07SJeff Roberson	def nexttype(self, type):
684ec5cae07SJeff Roberson		next = self.next()
685ec5cae07SJeff Roberson		while (next != None and next.type != type):
686ec5cae07SJeff Roberson			next = next.next()
687ec5cae07SJeff Roberson		return (next)
688ec5cae07SJeff Roberson
689dcee3bd3SJeff Roberson	def prev(self):
690dcee3bd3SJeff Roberson		return self.source.eventat(self.idx - 1)
691dcee3bd3SJeff Roberson
692dcee3bd3SJeff Roberson	def displayref(self, canvas):
693dcee3bd3SJeff Roberson		if (self.dispcnt == 0):
694dcee3bd3SJeff Roberson			canvas.itemconfigure(self.item, width=2)
695dcee3bd3SJeff Roberson		self.dispcnt += 1
696dcee3bd3SJeff Roberson
697dcee3bd3SJeff Roberson	def displayunref(self, canvas):
698dcee3bd3SJeff Roberson		self.dispcnt -= 1
699dcee3bd3SJeff Roberson		if (self.dispcnt == 0):
700dcee3bd3SJeff Roberson			canvas.itemconfigure(self.item, width=0)
701dcee3bd3SJeff Roberson			canvas.tag_raise("point", "state")
702dcee3bd3SJeff Roberson
703dcee3bd3SJeff Roberson	def getlinked(self):
704ec5cae07SJeff Roberson		for attr in self.attrs:
705ec5cae07SJeff Roberson			if (attr[0] != "linkedto"):
706ec5cae07SJeff Roberson				continue
707ec5cae07SJeff Roberson			source = ktrfile.findid(attr[1])
708ec5cae07SJeff Roberson			return source.findevent(self.timestamp)
709ec5cae07SJeff Roberson		return None
710dcee3bd3SJeff Roberson
711dcee3bd3SJeff Robersonclass PointEvent(Event):
712ec5cae07SJeff Roberson	type = "point"
713ec5cae07SJeff Roberson	def __init__(self, source, name, cpu, timestamp, attrs):
714ec5cae07SJeff Roberson		Event.__init__(self, source, name, cpu, timestamp, attrs)
715dcee3bd3SJeff Roberson
716dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
717ec5cae07SJeff Roberson		color = colormap.lookup(self.name)
718932f0fa2SJeff Roberson		l = canvas.create_oval(xpos - XY_POINT, ypos,
719932f0fa2SJeff Roberson		    xpos + XY_POINT, ypos - (XY_POINT * 2),
72050d670daSJeff Roberson		    fill=color, width=0,
721932f0fa2SJeff Roberson		    tags=("event", self.type, self.name, self.source.tag))
722ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, l)
723dcee3bd3SJeff Roberson
724ec5cae07SJeff Roberson		return xpos
725dcee3bd3SJeff Roberson
726dcee3bd3SJeff Robersonclass StateEvent(Event):
727ec5cae07SJeff Roberson	type = "state"
728ec5cae07SJeff Roberson	def __init__(self, source, name, cpu, timestamp, attrs):
729ec5cae07SJeff Roberson		Event.__init__(self, source, name, cpu, timestamp, attrs)
730dcee3bd3SJeff Roberson
731dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
732ec5cae07SJeff Roberson		next = self.nexttype("state")
733dcee3bd3SJeff Roberson		if (next == None):
734ec5cae07SJeff Roberson			return (xpos)
73550d670daSJeff Roberson		self.duration = duration = next.timestamp - self.timestamp
736ec5cae07SJeff Roberson		self.attrs.insert(0, ("duration", ticks2sec(duration)))
737ec5cae07SJeff Roberson		color = colormap.lookup(self.name)
738ec5cae07SJeff Roberson		if (duration < 0):
739ec5cae07SJeff Roberson			duration = 0
740749f65e3SCraig Rodrigues			print("Unsynchronized timestamp")
741749f65e3SCraig Rodrigues			print(self.cpu, self.timestamp)
742749f65e3SCraig Rodrigues			print(next.cpu, next.timestamp)
743ec5cae07SJeff Roberson		delta = duration / canvas.ratio
744dcee3bd3SJeff Roberson		l = canvas.create_rectangle(xpos, ypos,
745ec5cae07SJeff Roberson		    xpos + delta, ypos - 10, fill=color, width=0,
746932f0fa2SJeff Roberson		    tags=("event", self.type, self.name, self.source.tag))
747ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, l)
748dcee3bd3SJeff Roberson
749dcee3bd3SJeff Roberson		return (xpos + delta)
750dcee3bd3SJeff Roberson
751ec5cae07SJeff Robersonclass CountEvent(Event):
752ec5cae07SJeff Roberson	type = "count"
753ec5cae07SJeff Roberson	def __init__(self, source, count, cpu, timestamp, attrs):
754ec5cae07SJeff Roberson		count = int(count)
755ec5cae07SJeff Roberson		self.count = count
756ec5cae07SJeff Roberson		Event.__init__(self, source, "count", cpu, timestamp, attrs)
757dcee3bd3SJeff Roberson
758dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
759ec5cae07SJeff Roberson		next = self.nexttype("count")
760ec5cae07SJeff Roberson		if (next == None):
761ec5cae07SJeff Roberson			return (xpos)
762ec5cae07SJeff Roberson		color = colormap.lookup("count")
76350d670daSJeff Roberson		self.duration = duration = next.timestamp - self.timestamp
76416ef0f3bSJeff Roberson		if (duration < 0):
76516ef0f3bSJeff Roberson			duration = 0
766749f65e3SCraig Rodrigues			print("Unsynchronized timestamp")
767749f65e3SCraig Rodrigues			print(self.cpu, self.timestamp)
768749f65e3SCraig Rodrigues			print(next.cpu, next.timestamp)
769ec5cae07SJeff Roberson		self.attrs.insert(0, ("count", self.count))
770ec5cae07SJeff Roberson		self.attrs.insert(1, ("duration", ticks2sec(duration)))
771ec5cae07SJeff Roberson		delta = duration / canvas.ratio
772dcee3bd3SJeff Roberson		yhight = self.source.yscale() * self.count
773dcee3bd3SJeff Roberson		l = canvas.create_rectangle(xpos, ypos - yhight,
774ec5cae07SJeff Roberson		    xpos + delta, ypos, fill=color, width=0,
775932f0fa2SJeff Roberson		    tags=("event", self.type, self.name, self.source.tag))
776ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, l)
777dcee3bd3SJeff Roberson		return (xpos + delta)
778dcee3bd3SJeff Roberson
779ec5cae07SJeff Robersonclass PadEvent(StateEvent):
780ec5cae07SJeff Roberson	type = "pad"
781ec5cae07SJeff Roberson	def __init__(self, source, cpu, timestamp, last=0):
782ec5cae07SJeff Roberson		if (last):
783ec5cae07SJeff Roberson			cpu = source.events[len(source.events) -1].cpu
784ec5cae07SJeff Roberson		else:
785ec5cae07SJeff Roberson			cpu = source.events[0].cpu
786ec5cae07SJeff Roberson		StateEvent.__init__(self, source, "pad", cpu, timestamp, [])
787dcee3bd3SJeff Roberson	def draw(self, canvas, xpos, ypos):
788dcee3bd3SJeff Roberson		next = self.next()
789dcee3bd3SJeff Roberson		if (next == None):
790dcee3bd3SJeff Roberson			return (xpos)
791ec5cae07SJeff Roberson		duration = next.timestamp - self.timestamp
792ec5cae07SJeff Roberson		delta = duration / canvas.ratio
793ec5cae07SJeff Roberson		Event.draw(self, canvas, xpos, ypos, None)
794dcee3bd3SJeff Roberson		return (xpos + delta)
795dcee3bd3SJeff Roberson
79650d670daSJeff Roberson
797*7e8ed296SAndriy Gapon@total_ordering
798dcee3bd3SJeff Robersonclass EventSource:
799ec5cae07SJeff Roberson	def __init__(self, group, id):
800ec5cae07SJeff Roberson		self.name = id
801dcee3bd3SJeff Roberson		self.events = []
802ec5cae07SJeff Roberson		self.cpuitems = []
8032977b8f9SJohn Baldwin		self.group = group
804ec5cae07SJeff Roberson		self.y = 0
805ec5cae07SJeff Roberson		self.item = None
8063d21f0f4SJeff Roberson		self.hidden = 0
80750d670daSJeff Roberson		self.tag = group + id
808dcee3bd3SJeff Roberson
809*7e8ed296SAndriy Gapon	def __lt__(self, other):
810*7e8ed296SAndriy Gapon		if other is None:
811*7e8ed296SAndriy Gapon			return False
812*7e8ed296SAndriy Gapon		return (self.group < other.group or
813*7e8ed296SAndriy Gapon				self.group == other.group and self.name < other.name)
814*7e8ed296SAndriy Gapon
815*7e8ed296SAndriy Gapon	def __eq__(self, other):
816*7e8ed296SAndriy Gapon		if other is None:
817*7e8ed296SAndriy Gapon			return False
818*7e8ed296SAndriy Gapon		return self.group == other.group and self.name == other.name
8192977b8f9SJohn Baldwin
8202977b8f9SJohn Baldwin	# It is much faster to append items to a list then to insert them
8212977b8f9SJohn Baldwin	# at the beginning.  As a result, we add events in reverse order
8222977b8f9SJohn Baldwin	# and then swap the list during fixup.
823dcee3bd3SJeff Roberson	def fixup(self):
8242977b8f9SJohn Baldwin		self.events.reverse()
825dcee3bd3SJeff Roberson
826ec5cae07SJeff Roberson	def addevent(self, event):
8272977b8f9SJohn Baldwin		self.events.append(event)
828dcee3bd3SJeff Roberson
829ec5cae07SJeff Roberson	def addlastevent(self, event):
8302977b8f9SJohn Baldwin		self.events.insert(0, event)
831dcee3bd3SJeff Roberson
832dcee3bd3SJeff Roberson	def draw(self, canvas, ypos):
833dcee3bd3SJeff Roberson		xpos = 10
834ec5cae07SJeff Roberson		cpux = 10
835ec5cae07SJeff Roberson		cpu = self.events[1].cpu
836dcee3bd3SJeff Roberson		for i in range(0, len(self.events)):
837dcee3bd3SJeff Roberson			self.events[i].idx = i
838dcee3bd3SJeff Roberson		for event in self.events:
839ec5cae07SJeff Roberson			if (event.cpu != cpu and event.cpu != -1):
840ec5cae07SJeff Roberson				self.drawcpu(canvas, cpu, cpux, xpos, ypos)
841ec5cae07SJeff Roberson				cpux = xpos
842ec5cae07SJeff Roberson				cpu = event.cpu
843dcee3bd3SJeff Roberson			xpos = event.draw(canvas, xpos, ypos)
844ec5cae07SJeff Roberson		self.drawcpu(canvas, cpu, cpux, xpos, ypos)
845dcee3bd3SJeff Roberson
846dcee3bd3SJeff Roberson	def drawname(self, canvas, ypos):
847ec5cae07SJeff Roberson		self.y = ypos
848dcee3bd3SJeff Roberson		ypos = ypos - (self.ysize() / 2)
849932f0fa2SJeff Roberson		self.item = canvas.create_text(X_BORDER, ypos, anchor="w",
850932f0fa2SJeff Roberson		    text=self.name)
851ec5cae07SJeff Roberson		return (self.item)
852dcee3bd3SJeff Roberson
853ec5cae07SJeff Roberson	def drawcpu(self, canvas, cpu, fromx, tox, ypos):
854ec5cae07SJeff Roberson		cpu = "CPU " + str(cpu)
855ec5cae07SJeff Roberson		color = cpucolormap.lookup(cpu)
856ec5cae07SJeff Roberson		# Create the cpu background colors default to hidden
857ec5cae07SJeff Roberson		l = canvas.create_rectangle(fromx,
858dcee3bd3SJeff Roberson		    ypos - self.ysize() - canvas.bdheight,
859ec5cae07SJeff Roberson		    tox, ypos + canvas.bdheight, fill=color, width=0,
860932f0fa2SJeff Roberson		    tags=("cpubg", cpu, self.tag), state="hidden")
861ec5cae07SJeff Roberson		self.cpuitems.append(l)
862ec5cae07SJeff Roberson
863ec5cae07SJeff Roberson	def move(self, canvas, xpos, ypos):
86450d670daSJeff Roberson		canvas.move(self.tag, xpos, ypos)
865ec5cae07SJeff Roberson
866ec5cae07SJeff Roberson	def movename(self, canvas, xpos, ypos):
867ec5cae07SJeff Roberson		self.y += ypos
868ec5cae07SJeff Roberson		canvas.move(self.item, xpos, ypos)
869dcee3bd3SJeff Roberson
870dcee3bd3SJeff Roberson	def ysize(self):
871932f0fa2SJeff Roberson		return (Y_EVENTSOURCE)
872dcee3bd3SJeff Roberson
873dcee3bd3SJeff Roberson	def eventat(self, i):
874c3db6aa6SJohn Baldwin		if (i >= len(self.events) or i < 0):
875dcee3bd3SJeff Roberson			return (None)
876dcee3bd3SJeff Roberson		event = self.events[i]
877dcee3bd3SJeff Roberson		return (event)
878dcee3bd3SJeff Roberson
879dcee3bd3SJeff Roberson	def findevent(self, timestamp):
880dcee3bd3SJeff Roberson		for event in self.events:
881ec5cae07SJeff Roberson			if (event.timestamp >= timestamp and event.type != "pad"):
882dcee3bd3SJeff Roberson				return (event)
883dcee3bd3SJeff Roberson		return (None)
884dcee3bd3SJeff Roberson
885dcee3bd3SJeff Robersonclass Counter(EventSource):
886ec5cae07SJeff Roberson	#
887ec5cae07SJeff Roberson	# Store a hash of counter groups that keeps the max value
888ec5cae07SJeff Roberson	# for a counter in this group for scaling purposes.
889ec5cae07SJeff Roberson	#
890ec5cae07SJeff Roberson	groups = {}
891ec5cae07SJeff Roberson	def __init__(self, group, id):
892dcee3bd3SJeff Roberson		try:
893ec5cae07SJeff Roberson			Counter.cnt = Counter.groups[group]
894dcee3bd3SJeff Roberson		except:
895ec5cae07SJeff Roberson			Counter.groups[group] = 0
896ec5cae07SJeff Roberson		EventSource.__init__(self, group, id)
897ec5cae07SJeff Roberson
898ec5cae07SJeff Roberson	def fixup(self):
899ec5cae07SJeff Roberson		for event in self.events:
900ec5cae07SJeff Roberson			if (event.type != "count"):
901ec5cae07SJeff Roberson				continue;
902ec5cae07SJeff Roberson			count = int(event.count)
903ec5cae07SJeff Roberson			if (count > Counter.groups[self.group]):
904ec5cae07SJeff Roberson				Counter.groups[self.group] = count
905ec5cae07SJeff Roberson		EventSource.fixup(self)
906dcee3bd3SJeff Roberson
90766835de4SSam Leffler	def ymax(self):
908ec5cae07SJeff Roberson		return (Counter.groups[self.group])
90966835de4SSam Leffler
910dcee3bd3SJeff Roberson	def ysize(self):
911932f0fa2SJeff Roberson		return (Y_COUNTER)
912dcee3bd3SJeff Roberson
913dcee3bd3SJeff Roberson	def yscale(self):
914ec5cae07SJeff Roberson		return (self.ysize() / self.ymax())
915dcee3bd3SJeff Roberson
916dcee3bd3SJeff Robersonclass KTRFile:
917dcee3bd3SJeff Roberson	def __init__(self, file):
9180482a607SJeff Roberson		self.timestamp_f = None
9190482a607SJeff Roberson		self.timestamp_l = None
9200199a61dSJohn Baldwin		self.locks = {}
921dcee3bd3SJeff Roberson		self.ticks = {}
922dcee3bd3SJeff Roberson		self.load = {}
92301e7fb47SScott Long		self.crit = {}
9240482a607SJeff Roberson		self.stathz = 0
925932f0fa2SJeff Roberson		self.eventcnt = 0
92616ef0f3bSJeff Roberson		self.taghash = {}
927dcee3bd3SJeff Roberson
928dcee3bd3SJeff Roberson		self.parse(file)
929dcee3bd3SJeff Roberson		self.fixup()
930dcee3bd3SJeff Roberson		global ticksps
9310482a607SJeff Roberson		ticksps = self.ticksps()
932932f0fa2SJeff Roberson		span = self.timespan()
933932f0fa2SJeff Roberson		ghz = float(ticksps) / 1000000000.0
934932f0fa2SJeff Roberson		#
935932f0fa2SJeff Roberson		# Update the title with some stats from the file
936932f0fa2SJeff Roberson		#
937932f0fa2SJeff Roberson		titlestr = "SchedGraph: "
938932f0fa2SJeff Roberson		titlestr += ticks2sec(span) + " at %.3f ghz, " % ghz
939932f0fa2SJeff Roberson		titlestr += str(len(sources)) + " event sources, "
940932f0fa2SJeff Roberson		titlestr += str(self.eventcnt) + " events"
941932f0fa2SJeff Roberson		root.title(titlestr)
942dcee3bd3SJeff Roberson
943dcee3bd3SJeff Roberson	def parse(self, file):
944dcee3bd3SJeff Roberson		try:
945dcee3bd3SJeff Roberson			ifp = open(file)
946dcee3bd3SJeff Roberson		except:
947749f65e3SCraig Rodrigues			print("Can't open", file)
948dcee3bd3SJeff Roberson			sys.exit(1)
949dcee3bd3SJeff Roberson
950ec5cae07SJeff Roberson		# quoteexp matches a quoted string, no escaping
951ec5cae07SJeff Roberson		quoteexp = "\"([^\"]*)\""
952dcee3bd3SJeff Roberson
953ec5cae07SJeff Roberson		#
954ec5cae07SJeff Roberson		# commaexp matches a quoted string OR the string up
955ec5cae07SJeff Roberson		# to the first ','
956ec5cae07SJeff Roberson		#
957ec5cae07SJeff Roberson		commaexp = "(?:" + quoteexp + "|([^,]+))"
958dcee3bd3SJeff Roberson
959ec5cae07SJeff Roberson		#
960ec5cae07SJeff Roberson		# colonstr matches a quoted string OR the string up
961ec5cae07SJeff Roberson		# to the first ':'
962ec5cae07SJeff Roberson		#
963ec5cae07SJeff Roberson		colonexp = "(?:" + quoteexp + "|([^:]+))"
964dcee3bd3SJeff Roberson
965ec5cae07SJeff Roberson		#
966ec5cae07SJeff Roberson		# Match various manditory parts of the KTR string this is
967ec5cae07SJeff Roberson		# fairly inflexible until you get to attributes to make
968ec5cae07SJeff Roberson		# parsing faster.
969ec5cae07SJeff Roberson		#
970ec5cae07SJeff Roberson		hdrexp = "\s*(\d+)\s+(\d+)\s+(\d+)\s+"
971ec5cae07SJeff Roberson		groupexp = "KTRGRAPH group:" + quoteexp + ", "
972ec5cae07SJeff Roberson		idexp = "id:" + quoteexp + ", "
973ec5cae07SJeff Roberson		typeexp = "([^:]+):" + commaexp + ", "
974ec5cae07SJeff Roberson		attribexp = "attributes: (.*)"
975dcee3bd3SJeff Roberson
976ec5cae07SJeff Roberson		#
977ec5cae07SJeff Roberson		# Matches optional attributes in the KTR string.  This
978ec5cae07SJeff Roberson		# tolerates more variance as the users supply these values.
979ec5cae07SJeff Roberson		#
980ec5cae07SJeff Roberson		attrexp = colonexp + "\s*:\s*(?:" + commaexp + ", (.*)|"
981ec5cae07SJeff Roberson		attrexp += quoteexp +"|(.*))"
982dcee3bd3SJeff Roberson
983ec5cae07SJeff Roberson		# Precompile regexp
984ec5cae07SJeff Roberson		ktrre = re.compile(hdrexp + groupexp + idexp + typeexp + attribexp)
985ec5cae07SJeff Roberson		attrre = re.compile(attrexp)
986dcee3bd3SJeff Roberson
98766835de4SSam Leffler		global lineno
98866835de4SSam Leffler		lineno = 0
989dbad07bfSJeff Roberson		for line in ifp.readlines():
99066835de4SSam Leffler			lineno += 1
991ec5cae07SJeff Roberson			if ((lineno % 2048) == 0):
99266835de4SSam Leffler				status.startup("Parsing line " + str(lineno))
993ec5cae07SJeff Roberson			m = ktrre.match(line);
9942e2e6cc9SJeff Roberson			if (m == None):
995749f65e3SCraig Rodrigues				print("Can't parse", lineno, line, end=' ')
996ec5cae07SJeff Roberson				continue;
997ec5cae07SJeff Roberson			(index, cpu, timestamp, group, id, type, dat, dat1, attrstring) = m.groups();
998ec5cae07SJeff Roberson			if (dat == None):
999ec5cae07SJeff Roberson				dat = dat1
1000ec5cae07SJeff Roberson			if (self.checkstamp(timestamp) == 0):
1001749f65e3SCraig Rodrigues				print("Bad timestamp at", lineno, ":", end=' ')
1002749f65e3SCraig Rodrigues				print(cpu, timestamp)
1003ec5cae07SJeff Roberson				continue
1004ec5cae07SJeff Roberson			#
1005ec5cae07SJeff Roberson			# Build the table of optional attributes
1006ec5cae07SJeff Roberson			#
1007ec5cae07SJeff Roberson			attrs = []
1008ec5cae07SJeff Roberson			while (attrstring != None):
1009ec5cae07SJeff Roberson				m = attrre.match(attrstring.strip())
1010ec5cae07SJeff Roberson				if (m == None):
1011ec5cae07SJeff Roberson					break;
1012ec5cae07SJeff Roberson				#
1013ec5cae07SJeff Roberson				# Name may or may not be quoted.
1014ec5cae07SJeff Roberson				#
1015ec5cae07SJeff Roberson				# For val we have four cases:
1016ec5cae07SJeff Roberson				# 1) quotes followed by comma and more
1017ec5cae07SJeff Roberson				#    attributes.
1018ec5cae07SJeff Roberson				# 2) no quotes followed by comma and more
1019ec5cae07SJeff Roberson				#    attributes.
1020ec5cae07SJeff Roberson				# 3) no more attributes or comma with quotes.
1021ec5cae07SJeff Roberson				# 4) no more attributes or comma without quotes.
1022ec5cae07SJeff Roberson				#
1023ec5cae07SJeff Roberson				(name, name1, val, val1, attrstring, end, end1) = m.groups();
1024ec5cae07SJeff Roberson				if (name == None):
1025ec5cae07SJeff Roberson					name = name1
1026ec5cae07SJeff Roberson				if (end == None):
1027ec5cae07SJeff Roberson					end = end1
1028ec5cae07SJeff Roberson				if (val == None):
1029ec5cae07SJeff Roberson					val = val1
1030ec5cae07SJeff Roberson				if (val == None):
1031ec5cae07SJeff Roberson					val = end
1032ec5cae07SJeff Roberson				if (name == "stathz"):
1033ec5cae07SJeff Roberson					self.setstathz(val, cpu)
1034ec5cae07SJeff Roberson				attrs.append((name, val))
1035ec5cae07SJeff Roberson			args = (dat, cpu, timestamp, attrs)
1036ec5cae07SJeff Roberson			e = self.makeevent(group, id, type, args)
1037ec5cae07SJeff Roberson			if (e == None):
1038749f65e3SCraig Rodrigues				print("Unknown type", type, lineno, line, end=' ')
1039dcee3bd3SJeff Roberson
1040ec5cae07SJeff Roberson	def makeevent(self, group, id, type, args):
1041ec5cae07SJeff Roberson		e = None
1042ec5cae07SJeff Roberson		source = self.makeid(group, id, type)
1043ec5cae07SJeff Roberson		if (type == "state"):
1044ec5cae07SJeff Roberson			e = StateEvent(source, *args)
1045ec5cae07SJeff Roberson		elif (type == "counter"):
1046ec5cae07SJeff Roberson			e = CountEvent(source, *args)
1047ec5cae07SJeff Roberson		elif (type == "point"):
1048ec5cae07SJeff Roberson			e = PointEvent(source, *args)
1049ec5cae07SJeff Roberson		if (e != None):
1050932f0fa2SJeff Roberson			self.eventcnt += 1
1051ec5cae07SJeff Roberson			source.addevent(e);
1052ec5cae07SJeff Roberson		return e
1053dcee3bd3SJeff Roberson
1054ec5cae07SJeff Roberson	def setstathz(self, val, cpu):
1055ec5cae07SJeff Roberson		self.stathz = int(val)
1056dcee3bd3SJeff Roberson		cpu = int(cpu)
1057dcee3bd3SJeff Roberson		try:
1058dcee3bd3SJeff Roberson			ticks = self.ticks[cpu]
1059dcee3bd3SJeff Roberson		except:
1060dcee3bd3SJeff Roberson			self.ticks[cpu] = 0
1061dcee3bd3SJeff Roberson		self.ticks[cpu] += 1
1062dcee3bd3SJeff Roberson
1063ec5cae07SJeff Roberson	def checkstamp(self, timestamp):
1064ec5cae07SJeff Roberson		timestamp = int(timestamp)
1065ec5cae07SJeff Roberson		if (self.timestamp_f == None):
1066ec5cae07SJeff Roberson			self.timestamp_f = timestamp;
106716ef0f3bSJeff Roberson		if (self.timestamp_l != None and
106816ef0f3bSJeff Roberson		    timestamp -2048> self.timestamp_l):
1069ec5cae07SJeff Roberson			return (0)
1070ec5cae07SJeff Roberson		self.timestamp_l = timestamp;
1071ec5cae07SJeff Roberson		return (1)
1072dcee3bd3SJeff Roberson
1073ec5cae07SJeff Roberson	def makeid(self, group, id, type):
107416ef0f3bSJeff Roberson		tag = group + id
1075aef675d8SCraig Rodrigues		if (tag in self.taghash):
107616ef0f3bSJeff Roberson			return self.taghash[tag]
1077ec5cae07SJeff Roberson		if (type == "counter"):
1078ec5cae07SJeff Roberson			source = Counter(group, id)
10790199a61dSJohn Baldwin		else:
1080ec5cae07SJeff Roberson			source = EventSource(group, id)
1081ec5cae07SJeff Roberson		sources.append(source)
108216ef0f3bSJeff Roberson		self.taghash[tag] = source
1083ec5cae07SJeff Roberson		return (source)
10840199a61dSJohn Baldwin
1085ec5cae07SJeff Roberson	def findid(self, id):
1086ec5cae07SJeff Roberson		for source in sources:
1087ec5cae07SJeff Roberson			if (source.name == id):
1088ec5cae07SJeff Roberson				return source
1089ec5cae07SJeff Roberson		return (None)
10900199a61dSJohn Baldwin
1091ec5cae07SJeff Roberson	def timespan(self):
1092ec5cae07SJeff Roberson		return (self.timestamp_f - self.timestamp_l);
10930199a61dSJohn Baldwin
1094ec5cae07SJeff Roberson	def ticksps(self):
1095ec5cae07SJeff Roberson		oneghz = 1000000000
1096ec5cae07SJeff Roberson		# Use user supplied clock first
1097ec5cae07SJeff Roberson		if (clockfreq != None):
1098ec5cae07SJeff Roberson			return int(clockfreq * oneghz)
10990199a61dSJohn Baldwin
1100ec5cae07SJeff Roberson		# Check for a discovered clock
110116ef0f3bSJeff Roberson		if (self.stathz != 0):
1102ec5cae07SJeff Roberson			return (self.timespan() / self.ticks[0]) * int(self.stathz)
1103ec5cae07SJeff Roberson		# Pretend we have a 1ns clock
1104749f65e3SCraig Rodrigues		print("WARNING: No clock discovered and no frequency ", end=' ')
1105749f65e3SCraig Rodrigues		print("specified via the command line.")
1106749f65e3SCraig Rodrigues		print("Using fake 1ghz clock")
1107ec5cae07SJeff Roberson		return (oneghz);
1108dcee3bd3SJeff Roberson
1109dcee3bd3SJeff Roberson	def fixup(self):
1110ec5cae07SJeff Roberson		for source in sources:
1111ec5cae07SJeff Roberson			e = PadEvent(source, -1, self.timestamp_l)
1112ec5cae07SJeff Roberson			source.addevent(e)
1113ec5cae07SJeff Roberson			e = PadEvent(source, -1, self.timestamp_f, last=1)
1114ec5cae07SJeff Roberson			source.addlastevent(e)
1115dcee3bd3SJeff Roberson			source.fixup()
1116ec5cae07SJeff Roberson		sources.sort()
1117ec5cae07SJeff Roberson
1118ec5cae07SJeff Robersonclass SchedNames(Canvas):
1119ec5cae07SJeff Roberson	def __init__(self, master, display):
1120ec5cae07SJeff Roberson		self.display = display
1121ec5cae07SJeff Roberson		self.parent = master
1122ec5cae07SJeff Roberson		self.bdheight = master.bdheight
1123ec5cae07SJeff Roberson		self.items = {}
1124ec5cae07SJeff Roberson		self.ysize = 0
1125ec5cae07SJeff Roberson		self.lines = []
1126ec5cae07SJeff Roberson		Canvas.__init__(self, master, width=120,
1127ec5cae07SJeff Roberson		    height=display["height"], bg='grey',
1128ec5cae07SJeff Roberson		    scrollregion=(0, 0, 50, 100))
1129ec5cae07SJeff Roberson
1130ec5cae07SJeff Roberson	def moveline(self, cur_y, y):
1131ec5cae07SJeff Roberson		for line in self.lines:
1132ec5cae07SJeff Roberson			(x0, y0, x1, y1) = self.coords(line)
1133ec5cae07SJeff Roberson			if (cur_y != y0):
1134ec5cae07SJeff Roberson				continue
1135ec5cae07SJeff Roberson			self.move(line, 0, y)
1136ec5cae07SJeff Roberson			return
1137ec5cae07SJeff Roberson
1138ec5cae07SJeff Roberson	def draw(self):
1139ec5cae07SJeff Roberson		status.startup("Drawing names")
1140ec5cae07SJeff Roberson		ypos = 0
1141ec5cae07SJeff Roberson		self.configure(scrollregion=(0, 0,
1142ec5cae07SJeff Roberson		    self["width"], self.display.ysize()))
1143ec5cae07SJeff Roberson		for source in sources:
1144ec5cae07SJeff Roberson			l = self.create_line(0, ypos, self["width"], ypos,
1145ec5cae07SJeff Roberson			    width=1, fill="black", tags=("all","sources"))
1146ec5cae07SJeff Roberson			self.lines.append(l)
1147ec5cae07SJeff Roberson			ypos += self.bdheight
1148ec5cae07SJeff Roberson			ypos += source.ysize()
1149ec5cae07SJeff Roberson			t = source.drawname(self, ypos)
1150ec5cae07SJeff Roberson			self.items[t] = source
1151ec5cae07SJeff Roberson			ypos += self.bdheight
1152ec5cae07SJeff Roberson		self.ysize = ypos
1153ec5cae07SJeff Roberson		self.create_line(0, ypos, self["width"], ypos,
1154ec5cae07SJeff Roberson		    width=1, fill="black", tags=("all",))
1155ec5cae07SJeff Roberson		self.bind("<Button-1>", self.master.mousepress);
115650d670daSJeff Roberson		self.bind("<Button-3>", self.master.mousepressright);
1157ec5cae07SJeff Roberson		self.bind("<ButtonRelease-1>", self.master.mouserelease);
1158ec5cae07SJeff Roberson		self.bind("<B1-Motion>", self.master.mousemotion);
1159ec5cae07SJeff Roberson
11603d21f0f4SJeff Roberson	def updatescroll(self):
11613d21f0f4SJeff Roberson		self.configure(scrollregion=(0, 0,
11623d21f0f4SJeff Roberson		    self["width"], self.display.ysize()))
11633d21f0f4SJeff Roberson
1164dcee3bd3SJeff Roberson
1165dcee3bd3SJeff Robersonclass SchedDisplay(Canvas):
1166dcee3bd3SJeff Roberson	def __init__(self, master):
11672977b8f9SJohn Baldwin		self.ratio = 1
11682977b8f9SJohn Baldwin		self.parent = master
1169ec5cae07SJeff Roberson		self.bdheight = master.bdheight
1170ec5cae07SJeff Roberson		self.items = {}
1171ec5cae07SJeff Roberson		self.lines = []
1172dcee3bd3SJeff Roberson		Canvas.__init__(self, master, width=800, height=500, bg='grey',
1173dcee3bd3SJeff Roberson		     scrollregion=(0, 0, 800, 500))
1174dcee3bd3SJeff Roberson
1175ec5cae07SJeff Roberson	def prepare(self):
1176ec5cae07SJeff Roberson		#
11772977b8f9SJohn Baldwin		# Compute a ratio to ensure that the file's timespan fits into
11782977b8f9SJohn Baldwin		# 2^31.  Although python may handle larger values for X
11792977b8f9SJohn Baldwin		# values, the Tk internals do not.
1180ec5cae07SJeff Roberson		#
11812977b8f9SJohn Baldwin		self.ratio = (ktrfile.timespan() - 1) / 2**31 + 1
11822977b8f9SJohn Baldwin
1183dcee3bd3SJeff Roberson	def draw(self):
1184dcee3bd3SJeff Roberson		ypos = 0
1185dcee3bd3SJeff Roberson		xsize = self.xsize()
1186ec5cae07SJeff Roberson		for source in sources:
1187dcee3bd3SJeff Roberson			status.startup("Drawing " + source.name)
1188ec5cae07SJeff Roberson			l = self.create_line(0, ypos, xsize, ypos,
1189dcee3bd3SJeff Roberson			    width=1, fill="black", tags=("all",))
1190ec5cae07SJeff Roberson			self.lines.append(l)
1191dcee3bd3SJeff Roberson			ypos += self.bdheight
1192dcee3bd3SJeff Roberson			ypos += source.ysize()
1193dcee3bd3SJeff Roberson			source.draw(self, ypos)
1194dcee3bd3SJeff Roberson			ypos += self.bdheight
1195dcee3bd3SJeff Roberson		self.tag_raise("point", "state")
1196932f0fa2SJeff Roberson		self.tag_lower("cpubg", ALL)
1197dcee3bd3SJeff Roberson		self.create_line(0, ypos, xsize, ypos,
1198932f0fa2SJeff Roberson		    width=1, fill="black", tags=("lines",))
1199dcee3bd3SJeff Roberson		self.tag_bind("event", "<Enter>", self.mouseenter)
1200dcee3bd3SJeff Roberson		self.tag_bind("event", "<Leave>", self.mouseexit)
1201ec5cae07SJeff Roberson		self.bind("<Button-1>", self.mousepress)
120250d670daSJeff Roberson		self.bind("<Button-3>", self.master.mousepressright);
12032977b8f9SJohn Baldwin		self.bind("<Button-4>", self.wheelup)
12042977b8f9SJohn Baldwin		self.bind("<Button-5>", self.wheeldown)
1205ec5cae07SJeff Roberson		self.bind("<ButtonRelease-1>", self.master.mouserelease);
1206ec5cae07SJeff Roberson		self.bind("<B1-Motion>", self.master.mousemotion);
1207ec5cae07SJeff Roberson
1208ec5cae07SJeff Roberson	def moveline(self, cur_y, y):
1209ec5cae07SJeff Roberson		for line in self.lines:
1210ec5cae07SJeff Roberson			(x0, y0, x1, y1) = self.coords(line)
1211ec5cae07SJeff Roberson			if (cur_y != y0):
1212ec5cae07SJeff Roberson				continue
1213ec5cae07SJeff Roberson			self.move(line, 0, y)
1214ec5cae07SJeff Roberson			return
1215dcee3bd3SJeff Roberson
1216dcee3bd3SJeff Roberson	def mouseenter(self, event):
1217dcee3bd3SJeff Roberson		item, = self.find_withtag(CURRENT)
1218ec5cae07SJeff Roberson		self.items[item].mouseenter(self)
1219dcee3bd3SJeff Roberson
1220dcee3bd3SJeff Roberson	def mouseexit(self, event):
1221dcee3bd3SJeff Roberson		item, = self.find_withtag(CURRENT)
1222ec5cae07SJeff Roberson		self.items[item].mouseexit(self)
1223dcee3bd3SJeff Roberson
1224dcee3bd3SJeff Roberson	def mousepress(self, event):
1225ec5cae07SJeff Roberson		# Find out what's beneath us
1226ec5cae07SJeff Roberson		items = self.find_withtag(CURRENT)
1227ec5cae07SJeff Roberson		if (len(items) == 0):
1228ec5cae07SJeff Roberson			self.master.mousepress(event)
1229ec5cae07SJeff Roberson			return
1230ec5cae07SJeff Roberson		# Only grab mouse presses for things with event tags.
1231ec5cae07SJeff Roberson		item = items[0]
1232ec5cae07SJeff Roberson		tags = self.gettags(item)
1233ec5cae07SJeff Roberson		for tag in tags:
1234ec5cae07SJeff Roberson			if (tag == "event"):
1235ec5cae07SJeff Roberson				self.items[item].mousepress(self)
1236ec5cae07SJeff Roberson				return
1237ec5cae07SJeff Roberson		# Leave the rest to the master window
1238ec5cae07SJeff Roberson		self.master.mousepress(event)
1239dcee3bd3SJeff Roberson
12402977b8f9SJohn Baldwin	def wheeldown(self, event):
12412977b8f9SJohn Baldwin		self.parent.display_yview("scroll", 1, "units")
12422977b8f9SJohn Baldwin
12432977b8f9SJohn Baldwin	def wheelup(self, event):
12442977b8f9SJohn Baldwin		self.parent.display_yview("scroll", -1, "units")
12452977b8f9SJohn Baldwin
1246dcee3bd3SJeff Roberson	def xsize(self):
1247932f0fa2SJeff Roberson		return ((ktrfile.timespan() / self.ratio) + (X_BORDER * 2))
1248dcee3bd3SJeff Roberson
1249dcee3bd3SJeff Roberson	def ysize(self):
1250dcee3bd3SJeff Roberson		ysize = 0
1251ec5cae07SJeff Roberson		for source in sources:
12523d21f0f4SJeff Roberson			if (source.hidden == 1):
12533d21f0f4SJeff Roberson				continue
12543d21f0f4SJeff Roberson			ysize += self.parent.sourcesize(source)
1255ec5cae07SJeff Roberson		return ysize
1256dcee3bd3SJeff Roberson
1257dcee3bd3SJeff Roberson	def scaleset(self, ratio):
1258ec5cae07SJeff Roberson		if (ktrfile == None):
1259dcee3bd3SJeff Roberson			return
1260dcee3bd3SJeff Roberson		oldratio = self.ratio
1261ec5cae07SJeff Roberson		xstart, xend = self.xview()
1262ec5cae07SJeff Roberson		midpoint = xstart + ((xend - xstart) / 2)
1263dcee3bd3SJeff Roberson
1264dcee3bd3SJeff Roberson		self.ratio = ratio
12653d21f0f4SJeff Roberson		self.updatescroll()
1266932f0fa2SJeff Roberson		self.scale(ALL, 0, 0, float(oldratio) / ratio, 1)
1267dcee3bd3SJeff Roberson
1268ec5cae07SJeff Roberson		xstart, xend = self.xview()
1269ec5cae07SJeff Roberson		xsize = (xend - xstart) / 2
1270ec5cae07SJeff Roberson		self.xview_moveto(midpoint - xsize)
1271dcee3bd3SJeff Roberson
12723d21f0f4SJeff Roberson	def updatescroll(self):
12733d21f0f4SJeff Roberson		self.configure(scrollregion=(0, 0, self.xsize(), self.ysize()))
12743d21f0f4SJeff Roberson
1275dcee3bd3SJeff Roberson	def scaleget(self):
1276dcee3bd3SJeff Roberson		return self.ratio
1277dcee3bd3SJeff Roberson
1278ec5cae07SJeff Roberson	def getcolor(self, tag):
1279ec5cae07SJeff Roberson		return self.itemcget(tag, "fill")
1280ec5cae07SJeff Roberson
1281ec5cae07SJeff Roberson	def getstate(self, tag):
1282ec5cae07SJeff Roberson		return self.itemcget(tag, "state")
1283ec5cae07SJeff Roberson
1284dcee3bd3SJeff Roberson	def setcolor(self, tag, color):
1285dcee3bd3SJeff Roberson		self.itemconfigure(tag, state="normal", fill=color)
1286dcee3bd3SJeff Roberson
1287dcee3bd3SJeff Roberson	def hide(self, tag):
1288dcee3bd3SJeff Roberson		self.itemconfigure(tag, state="hidden")
1289dcee3bd3SJeff Roberson
1290dcee3bd3SJeff Robersonclass GraphMenu(Frame):
1291dcee3bd3SJeff Roberson	def __init__(self, master):
1292dcee3bd3SJeff Roberson		Frame.__init__(self, master, bd=2, relief=RAISED)
129350d670daSJeff Roberson		self.conf = Menubutton(self, text="Configure")
129450d670daSJeff Roberson		self.confmenu = Menu(self.conf, tearoff=0)
129550d670daSJeff Roberson		self.confmenu.add_command(label="Event Colors",
1296dcee3bd3SJeff Roberson		    command=self.econf)
129750d670daSJeff Roberson		self.confmenu.add_command(label="CPU Colors",
1298ec5cae07SJeff Roberson		    command=self.cconf)
129950d670daSJeff Roberson		self.confmenu.add_command(label="Source Configure",
13003d21f0f4SJeff Roberson		    command=self.sconf)
130150d670daSJeff Roberson		self.conf["menu"] = self.confmenu
130250d670daSJeff Roberson		self.conf.pack(side=LEFT)
1303dcee3bd3SJeff Roberson
1304dcee3bd3SJeff Roberson	def econf(self):
1305ec5cae07SJeff Roberson		ColorConfigure(eventcolors, "Event Display Configuration")
1306ec5cae07SJeff Roberson
1307ec5cae07SJeff Roberson	def cconf(self):
1308ec5cae07SJeff Roberson		ColorConfigure(cpucolors, "CPU Background Colors")
1309dcee3bd3SJeff Roberson
13103d21f0f4SJeff Roberson	def sconf(self):
13113d21f0f4SJeff Roberson		SourceConfigure()
13123d21f0f4SJeff Roberson
1313dcee3bd3SJeff Robersonclass SchedGraph(Frame):
1314dcee3bd3SJeff Roberson	def __init__(self, master):
1315dcee3bd3SJeff Roberson		Frame.__init__(self, master)
1316dcee3bd3SJeff Roberson		self.menu = None
1317dcee3bd3SJeff Roberson		self.names = None
1318dcee3bd3SJeff Roberson		self.display = None
1319dcee3bd3SJeff Roberson		self.scale = None
1320dcee3bd3SJeff Roberson		self.status = None
1321932f0fa2SJeff Roberson		self.bdheight = Y_BORDER
1322ec5cae07SJeff Roberson		self.clicksource = None
1323ec5cae07SJeff Roberson		self.lastsource = None
1324dcee3bd3SJeff Roberson		self.pack(expand=1, fill="both")
1325dcee3bd3SJeff Roberson		self.buildwidgets()
1326dcee3bd3SJeff Roberson		self.layout()
13279799411bSJohn Baldwin		self.bind_all("<Control-q>", self.quitcb)
13289799411bSJohn Baldwin
13299799411bSJohn Baldwin	def quitcb(self, event):
13309799411bSJohn Baldwin		self.quit()
1331dcee3bd3SJeff Roberson
1332dcee3bd3SJeff Roberson	def buildwidgets(self):
1333dcee3bd3SJeff Roberson		global status
1334dcee3bd3SJeff Roberson		self.menu = GraphMenu(self)
1335dcee3bd3SJeff Roberson		self.display = SchedDisplay(self)
1336ec5cae07SJeff Roberson		self.names = SchedNames(self, self.display)
1337dcee3bd3SJeff Roberson		self.scale = Scaler(self, self.display)
1338dcee3bd3SJeff Roberson		status = self.status = Status(self)
1339dcee3bd3SJeff Roberson		self.scrollY = Scrollbar(self, orient="vertical",
1340dcee3bd3SJeff Roberson		    command=self.display_yview)
1341dcee3bd3SJeff Roberson		self.display.scrollX = Scrollbar(self, orient="horizontal",
1342dcee3bd3SJeff Roberson		    command=self.display.xview)
1343dcee3bd3SJeff Roberson		self.display["xscrollcommand"] = self.display.scrollX.set
1344dcee3bd3SJeff Roberson		self.display["yscrollcommand"] = self.scrollY.set
1345dcee3bd3SJeff Roberson		self.names["yscrollcommand"] = self.scrollY.set
1346dcee3bd3SJeff Roberson
1347dcee3bd3SJeff Roberson	def layout(self):
1348dcee3bd3SJeff Roberson		self.columnconfigure(1, weight=1)
1349dcee3bd3SJeff Roberson		self.rowconfigure(1, weight=1)
1350dcee3bd3SJeff Roberson		self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W)
1351dcee3bd3SJeff Roberson		self.names.grid(row=1, column=0, sticky=N+S)
1352dcee3bd3SJeff Roberson		self.display.grid(row=1, column=1, sticky=W+E+N+S)
1353dcee3bd3SJeff Roberson		self.scrollY.grid(row=1, column=2, sticky=N+S)
1354dcee3bd3SJeff Roberson		self.display.scrollX.grid(row=2, column=0, columnspan=2,
1355dcee3bd3SJeff Roberson		    sticky=E+W)
1356dcee3bd3SJeff Roberson		self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W)
1357dcee3bd3SJeff Roberson		self.status.grid(row=4, column=0, columnspan=3, sticky=E+W)
1358dcee3bd3SJeff Roberson
1359ec5cae07SJeff Roberson	def draw(self):
1360dcee3bd3SJeff Roberson		self.master.update()
1361ec5cae07SJeff Roberson		self.display.prepare()
1362ec5cae07SJeff Roberson		self.names.draw()
1363dcee3bd3SJeff Roberson		self.display.draw()
1364ec5cae07SJeff Roberson		self.status.startup("")
136516ef0f3bSJeff Roberson		#
136616ef0f3bSJeff Roberson		# Configure scale related values
136716ef0f3bSJeff Roberson		#
136816ef0f3bSJeff Roberson		scalemax = ktrfile.timespan() / int(self.display["width"])
136916ef0f3bSJeff Roberson		width = int(root.geometry().split('x')[0])
137016ef0f3bSJeff Roberson		self.constwidth = width - int(self.display["width"])
137116ef0f3bSJeff Roberson		self.scale.setmax(scalemax)
137216ef0f3bSJeff Roberson		self.scale.set(scalemax)
1373dcee3bd3SJeff Roberson		self.display.xview_moveto(0)
137416ef0f3bSJeff Roberson		self.bind("<Configure>", self.resize)
1375dcee3bd3SJeff Roberson
1376ec5cae07SJeff Roberson	def mousepress(self, event):
1377ec5cae07SJeff Roberson		self.clicksource = self.sourceat(event.y)
1378ec5cae07SJeff Roberson
137950d670daSJeff Roberson	def mousepressright(self, event):
138050d670daSJeff Roberson		source = self.sourceat(event.y)
138150d670daSJeff Roberson		if (source == None):
138250d670daSJeff Roberson			return
138350d670daSJeff Roberson		SourceContext(event, source)
138450d670daSJeff Roberson
1385ec5cae07SJeff Roberson	def mouserelease(self, event):
1386ec5cae07SJeff Roberson		if (self.clicksource == None):
1387ec5cae07SJeff Roberson			return
1388ec5cae07SJeff Roberson		newsource = self.sourceat(event.y)
1389ec5cae07SJeff Roberson		if (self.clicksource != newsource):
1390ec5cae07SJeff Roberson			self.sourceswap(self.clicksource, newsource)
1391ec5cae07SJeff Roberson		self.clicksource = None
1392ec5cae07SJeff Roberson		self.lastsource = None
1393ec5cae07SJeff Roberson
1394ec5cae07SJeff Roberson	def mousemotion(self, event):
1395ec5cae07SJeff Roberson		if (self.clicksource == None):
1396ec5cae07SJeff Roberson			return
1397ec5cae07SJeff Roberson		newsource = self.sourceat(event.y)
1398ec5cae07SJeff Roberson		#
1399ec5cae07SJeff Roberson		# If we get a None source they moved off the page.
1400ec5cae07SJeff Roberson		# swapsource() can't handle moving multiple items so just
1401ec5cae07SJeff Roberson		# pretend we never clicked on anything to begin with so the
1402ec5cae07SJeff Roberson		# user can't mouseover a non-contiguous area.
1403ec5cae07SJeff Roberson		#
1404ec5cae07SJeff Roberson		if (newsource == None):
1405ec5cae07SJeff Roberson			self.clicksource = None
1406ec5cae07SJeff Roberson			self.lastsource = None
1407ec5cae07SJeff Roberson			return
1408ec5cae07SJeff Roberson		if (newsource == self.lastsource):
1409ec5cae07SJeff Roberson			return;
1410ec5cae07SJeff Roberson		self.lastsource = newsource
1411ec5cae07SJeff Roberson		if (newsource != self.clicksource):
1412ec5cae07SJeff Roberson			self.sourceswap(self.clicksource, newsource)
1413ec5cae07SJeff Roberson
1414ec5cae07SJeff Roberson	# These are here because this object controls layout
1415ec5cae07SJeff Roberson	def sourcestart(self, source):
1416ec5cae07SJeff Roberson		return source.y - self.bdheight - source.ysize()
1417ec5cae07SJeff Roberson
1418ec5cae07SJeff Roberson	def sourceend(self, source):
1419ec5cae07SJeff Roberson		return source.y + self.bdheight
1420ec5cae07SJeff Roberson
1421ec5cae07SJeff Roberson	def sourcesize(self, source):
1422ec5cae07SJeff Roberson		return (self.bdheight * 2) + source.ysize()
1423ec5cae07SJeff Roberson
1424ec5cae07SJeff Roberson	def sourceswap(self, source1, source2):
1425ec5cae07SJeff Roberson		# Sort so we always know which one is on top.
1426ec5cae07SJeff Roberson		if (source2.y < source1.y):
1427ec5cae07SJeff Roberson			swap = source1
1428ec5cae07SJeff Roberson			source1 = source2
1429ec5cae07SJeff Roberson			source2 = swap
1430ec5cae07SJeff Roberson		# Only swap adjacent sources
1431ec5cae07SJeff Roberson		if (self.sourceend(source1) != self.sourcestart(source2)):
1432ec5cae07SJeff Roberson			return
1433ec5cae07SJeff Roberson		# Compute start coordinates and target coordinates
1434ec5cae07SJeff Roberson		y1 = self.sourcestart(source1)
1435ec5cae07SJeff Roberson		y2 = self.sourcestart(source2)
1436ec5cae07SJeff Roberson		y1targ = y1 + self.sourcesize(source2)
1437ec5cae07SJeff Roberson		y2targ = y1
1438ec5cae07SJeff Roberson		#
1439ec5cae07SJeff Roberson		# If the sizes are not equal, adjust the start of the lower
1440ec5cae07SJeff Roberson		# source to account for the lost/gained space.
1441ec5cae07SJeff Roberson		#
1442ec5cae07SJeff Roberson		if (source1.ysize() != source2.ysize()):
1443ec5cae07SJeff Roberson			diff = source2.ysize() - source1.ysize()
1444ec5cae07SJeff Roberson			self.names.moveline(y2, diff);
1445ec5cae07SJeff Roberson			self.display.moveline(y2, diff)
1446ec5cae07SJeff Roberson		source1.move(self.display, 0, y1targ - y1)
1447ec5cae07SJeff Roberson		source2.move(self.display, 0, y2targ - y2)
1448ec5cae07SJeff Roberson		source1.movename(self.names, 0, y1targ - y1)
1449ec5cae07SJeff Roberson		source2.movename(self.names, 0, y2targ - y2)
1450ec5cae07SJeff Roberson
145150d670daSJeff Roberson	def sourcepicky(self, source):
14523d21f0f4SJeff Roberson		if (source.hidden == 0):
145350d670daSJeff Roberson			return self.sourcestart(source)
145450d670daSJeff Roberson		# Revert to group based sort
145550d670daSJeff Roberson		sources.sort()
14563d21f0f4SJeff Roberson		prev = None
14573d21f0f4SJeff Roberson		for s in sources:
14583d21f0f4SJeff Roberson			if (s == source):
14593d21f0f4SJeff Roberson				break
14603d21f0f4SJeff Roberson			if (s.hidden == 0):
14613d21f0f4SJeff Roberson				prev = s
14623d21f0f4SJeff Roberson		if (prev == None):
14633d21f0f4SJeff Roberson			newy = 0
14643d21f0f4SJeff Roberson		else:
14653d21f0f4SJeff Roberson			newy = self.sourcestart(prev) + self.sourcesize(prev)
146650d670daSJeff Roberson		return newy
146750d670daSJeff Roberson
146850d670daSJeff Roberson	def sourceshow(self, source):
146950d670daSJeff Roberson		if (source.hidden == 0):
147050d670daSJeff Roberson			return;
147150d670daSJeff Roberson		newy = self.sourcepicky(source)
14723d21f0f4SJeff Roberson		off = newy - self.sourcestart(source)
147350d670daSJeff Roberson		self.sourceshiftall(newy-1, self.sourcesize(source))
14743d21f0f4SJeff Roberson		self.sourceshift(source, off)
14753d21f0f4SJeff Roberson		source.hidden = 0
14763d21f0f4SJeff Roberson
147750d670daSJeff Roberson	#
147850d670daSJeff Roberson	# Optimized source show of multiple entries that only moves each
147950d670daSJeff Roberson	# existing entry once.  Doing sourceshow() iteratively is too
148050d670daSJeff Roberson	# expensive due to python's canvas.move().
148150d670daSJeff Roberson	#
148250d670daSJeff Roberson	def sourceshowlist(self, srclist):
1483*7e8ed296SAndriy Gapon		srclist.sort(key=attrgetter('y'))
148450d670daSJeff Roberson		startsize = []
148550d670daSJeff Roberson		for source in srclist:
148650d670daSJeff Roberson			if (source.hidden == 0):
148750d670daSJeff Roberson				srclist.remove(source)
148850d670daSJeff Roberson			startsize.append((self.sourcepicky(source),
148950d670daSJeff Roberson			    self.sourcesize(source)))
149050d670daSJeff Roberson
1491*7e8ed296SAndriy Gapon		sources.sort(key=attrgetter('y'), reverse=True)
149250d670daSJeff Roberson		self.status.startup("Updating display...");
149350d670daSJeff Roberson		for source in sources:
149450d670daSJeff Roberson			if (source.hidden == 1):
149550d670daSJeff Roberson				continue
149650d670daSJeff Roberson			nstart = self.sourcestart(source)
149750d670daSJeff Roberson			size = 0
149850d670daSJeff Roberson			for hidden in startsize:
149950d670daSJeff Roberson				(start, sz) = hidden
150050d670daSJeff Roberson				if (start <= nstart or start+sz <= nstart):
150150d670daSJeff Roberson					size += sz
150250d670daSJeff Roberson			self.sourceshift(source, size)
150350d670daSJeff Roberson		idx = 0
150450d670daSJeff Roberson		size = 0
150550d670daSJeff Roberson		for source in srclist:
150650d670daSJeff Roberson			(newy, sz) = startsize[idx]
150750d670daSJeff Roberson			off = (newy + size) - self.sourcestart(source)
150850d670daSJeff Roberson			self.sourceshift(source, off)
150950d670daSJeff Roberson			source.hidden = 0
151050d670daSJeff Roberson			size += sz
151150d670daSJeff Roberson			idx += 1
151216ef0f3bSJeff Roberson		self.updatescroll()
151350d670daSJeff Roberson		self.status.set("")
151450d670daSJeff Roberson
151550d670daSJeff Roberson	#
151650d670daSJeff Roberson	# Optimized source hide of multiple entries that only moves each
151750d670daSJeff Roberson	# remaining entry once.  Doing sourcehide() iteratively is too
151850d670daSJeff Roberson	# expensive due to python's canvas.move().
151950d670daSJeff Roberson	#
152050d670daSJeff Roberson	def sourcehidelist(self, srclist):
1521*7e8ed296SAndriy Gapon		srclist.sort(key=attrgetter('y'))
1522*7e8ed296SAndriy Gapon		sources.sort(key=attrgetter('y'))
152350d670daSJeff Roberson		startsize = []
152450d670daSJeff Roberson		off = len(sources) * 100
152550d670daSJeff Roberson		self.status.startup("Updating display...");
152650d670daSJeff Roberson		for source in srclist:
152750d670daSJeff Roberson			if (source.hidden == 1):
152850d670daSJeff Roberson				srclist.remove(source)
152950d670daSJeff Roberson			#
153050d670daSJeff Roberson			# Remember our old position so we can sort things
153150d670daSJeff Roberson			# below us when we're done.
153250d670daSJeff Roberson			#
153350d670daSJeff Roberson			startsize.append((self.sourcestart(source),
153450d670daSJeff Roberson			    self.sourcesize(source)))
153550d670daSJeff Roberson			self.sourceshift(source, off)
153650d670daSJeff Roberson			source.hidden = 1
153750d670daSJeff Roberson
153850d670daSJeff Roberson		idx = 0
153950d670daSJeff Roberson		size = 0
154050d670daSJeff Roberson		for hidden in startsize:
154150d670daSJeff Roberson			(start, sz) = hidden
154250d670daSJeff Roberson			size += sz
154350d670daSJeff Roberson			if (idx + 1 < len(startsize)):
154450d670daSJeff Roberson				(stop, sz) = startsize[idx+1]
154550d670daSJeff Roberson			else:
154650d670daSJeff Roberson				stop = self.display.ysize()
154750d670daSJeff Roberson			idx += 1
154850d670daSJeff Roberson			for source in sources:
154950d670daSJeff Roberson				nstart = self.sourcestart(source)
155050d670daSJeff Roberson				if (nstart < start or source.hidden == 1):
155150d670daSJeff Roberson					continue
155250d670daSJeff Roberson				if (nstart >= stop):
155350d670daSJeff Roberson					break;
155450d670daSJeff Roberson				self.sourceshift(source, -size)
155516ef0f3bSJeff Roberson		self.updatescroll()
155650d670daSJeff Roberson		self.status.set("")
155750d670daSJeff Roberson
15583d21f0f4SJeff Roberson	def sourcehide(self, source):
155950d670daSJeff Roberson		if (source.hidden == 1):
156050d670daSJeff Roberson			return;
15613d21f0f4SJeff Roberson		# Move it out of the visible area
15623d21f0f4SJeff Roberson		off = len(sources) * 100
15633d21f0f4SJeff Roberson		start = self.sourcestart(source)
15643d21f0f4SJeff Roberson		self.sourceshift(source, off)
15653d21f0f4SJeff Roberson		self.sourceshiftall(start, -self.sourcesize(source))
15663d21f0f4SJeff Roberson		source.hidden = 1
15673d21f0f4SJeff Roberson
15683d21f0f4SJeff Roberson	def sourceshift(self, source, off):
15693d21f0f4SJeff Roberson		start = self.sourcestart(source)
15703d21f0f4SJeff Roberson		source.move(self.display, 0, off)
15713d21f0f4SJeff Roberson		source.movename(self.names, 0, off)
15723d21f0f4SJeff Roberson		self.names.moveline(start, off);
15733d21f0f4SJeff Roberson		self.display.moveline(start, off)
157450d670daSJeff Roberson		#
157550d670daSJeff Roberson		# We update the idle tasks to shrink the dirtied area so
157650d670daSJeff Roberson		# it does not always include the entire screen.
157750d670daSJeff Roberson		#
157850d670daSJeff Roberson		self.names.update_idletasks()
157950d670daSJeff Roberson		self.display.update_idletasks()
15803d21f0f4SJeff Roberson
15813d21f0f4SJeff Roberson	def sourceshiftall(self, start, off):
158250d670daSJeff Roberson		self.status.startup("Updating display...");
15833d21f0f4SJeff Roberson		for source in sources:
15843d21f0f4SJeff Roberson			nstart = self.sourcestart(source)
15853d21f0f4SJeff Roberson			if (nstart < start):
15863d21f0f4SJeff Roberson				continue;
15873d21f0f4SJeff Roberson			self.sourceshift(source, off)
158816ef0f3bSJeff Roberson		self.updatescroll()
158950d670daSJeff Roberson		self.status.set("")
15903d21f0f4SJeff Roberson
1591ec5cae07SJeff Roberson	def sourceat(self, ypos):
1592ec5cae07SJeff Roberson		(start, end) = self.names.yview()
1593ec5cae07SJeff Roberson		starty = start * float(self.names.ysize)
1594ec5cae07SJeff Roberson		ypos += starty
1595ec5cae07SJeff Roberson		for source in sources:
15963d21f0f4SJeff Roberson			if (source.hidden == 1):
15973d21f0f4SJeff Roberson				continue;
1598ec5cae07SJeff Roberson			yend = self.sourceend(source)
1599ec5cae07SJeff Roberson			ystart = self.sourcestart(source)
1600ec5cae07SJeff Roberson			if (ypos >= ystart and ypos <= yend):
1601ec5cae07SJeff Roberson				return source
1602ec5cae07SJeff Roberson		return None
1603ec5cae07SJeff Roberson
1604dcee3bd3SJeff Roberson	def display_yview(self, *args):
1605dcee3bd3SJeff Roberson		self.names.yview(*args)
1606dcee3bd3SJeff Roberson		self.display.yview(*args)
1607dcee3bd3SJeff Roberson
160816ef0f3bSJeff Roberson	def resize(self, *args):
160916ef0f3bSJeff Roberson		width = int(root.geometry().split('x')[0])
161016ef0f3bSJeff Roberson		scalemax = ktrfile.timespan() / (width - self.constwidth)
161116ef0f3bSJeff Roberson		self.scale.setmax(scalemax)
161216ef0f3bSJeff Roberson
161316ef0f3bSJeff Roberson	def updatescroll(self):
161416ef0f3bSJeff Roberson		self.names.updatescroll()
161516ef0f3bSJeff Roberson		self.display.updatescroll()
161616ef0f3bSJeff Roberson
1617dcee3bd3SJeff Roberson	def setcolor(self, tag, color):
1618dcee3bd3SJeff Roberson		self.display.setcolor(tag, color)
1619dcee3bd3SJeff Roberson
1620dcee3bd3SJeff Roberson	def hide(self, tag):
1621dcee3bd3SJeff Roberson		self.display.hide(tag)
1622dcee3bd3SJeff Roberson
1623ec5cae07SJeff Roberson	def getcolor(self, tag):
1624ec5cae07SJeff Roberson		return self.display.getcolor(tag)
1625ec5cae07SJeff Roberson
1626ec5cae07SJeff Roberson	def getstate(self, tag):
1627ec5cae07SJeff Roberson		return self.display.getstate(tag)
1628ec5cae07SJeff Roberson
1629ec5cae07SJeff Robersonif (len(sys.argv) != 2 and len(sys.argv) != 3):
1630749f65e3SCraig Rodrigues	print("usage:", sys.argv[0], "<ktr file> [clock freq in ghz]")
1631dcee3bd3SJeff Roberson	sys.exit(1)
1632dcee3bd3SJeff Roberson
1633ec5cae07SJeff Robersonif (len(sys.argv) > 2):
1634ec5cae07SJeff Roberson	clockfreq = float(sys.argv[2])
1635ec5cae07SJeff Roberson
1636dcee3bd3SJeff Robersonroot = Tk()
1637932f0fa2SJeff Robersonroot.title("SchedGraph")
1638ec5cae07SJeff Robersoncolormap = Colormap(eventcolors)
1639ec5cae07SJeff Robersoncpucolormap = Colormap(cpucolors)
1640dcee3bd3SJeff Robersongraph = SchedGraph(root)
1641ec5cae07SJeff Robersonktrfile = KTRFile(sys.argv[1])
1642ec5cae07SJeff Robersongraph.draw()
1643dcee3bd3SJeff Robersonroot.mainloop()
1644