blob: 81693cc29762f8223c422845c26ed1a9531aca71 [file] [log] [blame]
# Oscilloscope
#
# Copyright 2008-2009 Red Hat, Inc.
#
# Arnaldo Carvalho de Melo <acme@redhat.com>
#
# Please check the tuna repository at:
# http://git.kernel.org/?p=linux/kernel/git/acme/tuna.git;a=tree
# For newer versions and to see it integrated with tuna
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
import gobject, gtk, os, sys
from matplotlib.backends.backend_gtkagg import \
FigureCanvasGTKAgg as figure_canvas
import matplotlib.figure, matplotlib.ticker, numpy
class histogram_frame(gtk.Frame):
def __init__(self, title = "Statistics", width = 780, height = 100,
max_value = 500, nr_entries = 10,
facecolor = "white"):
gtk.Frame.__init__(self, title)
self.fraction = int(max_value / nr_entries)
if self.fraction == 0:
self.fraction = max_value
nr_entries = 1
self.max_value = max_value
self.nr_entries = nr_entries
self.nr_samples = 0
table = gtk.Table(3, self.nr_entries + 1, False)
table.set_border_width(5)
table.set_row_spacings(5)
table.set_col_spacings(10)
self.add(table)
self.buckets = [ 0, ] * (nr_entries + 1)
self.buckets_bar = [ None, ] * (nr_entries + 1)
self.buckets_counter = [ None, ] * (nr_entries + 1)
prefix = "<="
for bucket in range(self.nr_entries + 1):
bucket_range = (bucket + 1) * self.fraction
if bucket_range > self.max_value:
prefix = ">"
bucket_range = self.max_value
label = gtk.Label("%s %d" % (prefix, bucket_range))
label.set_alignment(0, 1)
table.attach(label, 0, 1, bucket, bucket + 1, 0, 0, 0, 0)
self.buckets_bar[bucket] = gtk.ProgressBar()
table.attach(self.buckets_bar[bucket], 1, 2, bucket, bucket + 1, 0, 0, 0, 0)
self.buckets_counter[bucket] = gtk.Label("0")
label.set_alignment(0, 1)
table.attach(self.buckets_counter[bucket], 2, 3, bucket, bucket + 1, 0, 0, 0, 0)
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(facecolor))
def add_sample(self, sample):
if sample > self.max_value:
bucket = self.nr_entries
else:
bucket = int(sample / self.fraction)
self.nr_samples += 1
self.buckets[bucket] += 1
def refresh(self):
for bucket in range(self.nr_entries + 1):
self.buckets_counter[bucket].set_text(str(self.buckets[bucket]))
fraction = float(self.buckets[bucket]) / self.nr_samples
self.buckets_bar[bucket].set_fraction(fraction)
def reset(self):
self.buckets = [ 0, ] * (self.nr_entries + 1)
self.nr_samples = 0
class oscilloscope_frame(gtk.Frame):
def __init__(self, title = "Osciloscope", width = 780, height = 360,
nr_samples_on_screen = 250, graph_type = '-',
max_value = 500, plot_color = "lightgreen",
bg_color = "darkgreen", facecolor = "white",
ylabel = "Latency", picker = None):
gtk.Frame.__init__(self, title)
self.font = { 'fontname' : 'Liberation Sans',
'color' : 'b',
'fontweight' : 'bold',
'fontsize' : 10 }
self.max_value = max_value
self.nr_samples_on_screen = nr_samples_on_screen
self.ind = numpy.arange(nr_samples_on_screen)
self.samples = [ 0.0 ] * nr_samples_on_screen
figure = matplotlib.figure.Figure(figsize = (10, 4), dpi = 100,
facecolor = facecolor)
ax = figure.add_subplot(111)
self.ax = ax
ax.set_axis_bgcolor(bg_color)
self.on_screen_samples = ax.plot(self.ind, self.samples, graph_type,
color = plot_color,
picker = picker)
ax.set_ylim(0, max_value)
ax.set_ylabel(ylabel, self.font)
ax.set_xlabel("%d samples" % nr_samples_on_screen, self.font)
ax.set_xticklabels([])
ax.grid(True)
for label in ax.get_yticklabels():
label.set(fontsize = 8)
self.canvas = figure_canvas(figure) # a gtk.DrawingArea
self.canvas.set_size_request(width, height)
self.add(self.canvas)
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(facecolor))
self.nr_samples = 0
def add_sample(self, sample):
del self.samples[0]
self.samples.append(sample)
self.on_screen_samples[0].set_data(self.ind, self.samples)
self.nr_samples += 1
if self.nr_samples <= self.nr_samples_on_screen:
self.ax.set_xlabel("%d samples" % self.nr_samples, self.font)
def reset(self):
self.samples = [ 0.0 ] * self.nr_samples_on_screen
self.nr_samples = 0
self.on_screen_samples[0].set_data(self.ind, self.samples)
def refresh(self):
self.canvas.draw()
return
def add_table_row(table, row, label_text, label_value = "0"):
label = gtk.Label(label_text)
label.set_use_underline(True)
label.set_alignment(0, 1)
table.attach(label, 0, 1, row, row + 1, 0, 0, 0, 0)
label = gtk.Label(label_value)
table.attach(label, 1, 2, row, row + 1, 0, 0, 0, 0)
return label
class system_info_frame(gtk.Frame):
def __init__(self, title = "System", facecolor = "white"):
gtk.Frame.__init__(self, title)
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(facecolor))
table = gtk.Table(3, 2, False)
table.set_border_width(5)
table.set_row_spacings(5)
table.set_col_spacings(10)
self.add(table)
u = os.uname()
add_table_row(table, 0, "Kernel Release", u[2])
add_table_row(table, 1, "Architecture", u[4])
add_table_row(table, 2, "Machine", u[1])
class oscilloscope(gtk.Window):
def __init__(self, get_sample = None, width = 800, height = 500,
nr_samples_on_screen = 250,
graph_type = '-', title = "Osciloscope",
max_value = 500, plot_color = "lightgreen",
bg_color = "darkgreen", facecolor = "white",
ylabel = "Latency",
picker = None,
snapshot_samples = 0,
geometry = None, scale = True):
gtk.Window.__init__(self)
if geometry:
self.parse_geometry(geometry)
width, height = self.get_size()
else:
self.set_default_size(width, height)
self.get_sample = get_sample
self.max_value = max_value
self.snapshot_samples = snapshot_samples
self.scale = scale
self.set_title(title)
vbox = gtk.VBox()
vbox.set_border_width(8)
self.add(vbox)
stats_frame = gtk.Frame("Statistics")
stats_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(facecolor))
table = gtk.Table(3, 2, False)
table.set_border_width(5)
table.set_row_spacings(5)
table.set_col_spacings(10)
stats_frame.add(table)
self.min_label = add_table_row(table, 0, "Min")
self.avg_label = add_table_row(table, 1, "Avg")
self.max_label = add_table_row(table, 2, "Max")
help_frame = gtk.Frame("Help")
help_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(facecolor))
table = gtk.Table(4, 2, False)
table.set_border_width(5)
table.set_row_spacings(5)
table.set_col_spacings(10)
help_frame.add(table)
add_table_row(table, 0, "Space", "Pause")
add_table_row(table, 1, "S", "Snapshot")
add_table_row(table, 2, "R", "Reset")
add_table_row(table, 3, "Q", "Quit")
self.scope = oscilloscope_frame("Scope",
int(width * 0.94),
int(height * 0.64),
nr_samples_on_screen,
max_value = max_value,
graph_type = graph_type,
picker = picker,
ylabel = ylabel)
self.hist = histogram_frame("Histogram", 0, 0, nr_entries = 5,
max_value = max_value)
info_frame = system_info_frame()
vbox_help_info = gtk.VBox()
vbox_help_info.pack_start(info_frame, False, False)
vbox_help_info.pack_end(help_frame, False, False)
hbox = gtk.HBox()
hbox.pack_start(vbox_help_info, False, False)
hbox.pack_start(stats_frame, False, False)
hbox.pack_end(self.hist, True, True)
vbox.pack_start(self.scope, True, True)
vbox.pack_end(hbox, True, False)
self.show_all()
self.getting_samples = False
self.refreshing_screen = False
self.max = self.min = None
self.avg = 0
def add_sample(self, sample):
if not self.max or self.max < sample:
self.max = sample
if not self.min or self.min > sample:
self.min = sample
self.avg = (self.avg + sample) / 2
self.scope.add_sample(sample)
self.hist.add_sample(sample)
def refresh(self):
if self.scale and self.max > self.scope.max_value:
self.scope.max_value *= 2
self.scope.ax.set_ylim(0, self.scope.max_value)
self.scope.refresh()
self.hist.refresh()
while gtk.events_pending():
gtk.main_iteration()
def get_samples(self, fd, condition):
try:
sample = self.get_sample()
prev_min, prev_avg, prev_max = self.min, self.avg, self.max
self.add_sample(sample)
if self.refreshing_screen:
if self.min != prev_min:
self.min_label.set_text("%-6.3f" % self.min)
if self.avg != prev_avg:
self.avg_label.set_text("%-6.3f" % self.avg)
if self.max != prev_max:
self.max_label.set_text("%-6.3f" % self.max)
self.refresh()
if self.snapshot_samples == self.scope.nr_samples:
self.snapshot()
gtk.main_quit()
except:
print "invalid sample, check the input format"
pass
return self.getting_samples
def run(self, fd):
self.connect("key_press_event", self.key_press_event)
self.getting_samples = True
self.refreshing_screen = True
gobject.io_add_watch(fd, gobject.IO_IN | gobject.IO_PRI,
self.get_samples)
def freeze_screen(self, state = False):
self.refreshing_screen = state
def stop(self):
self.getting_samples = False
self.refreshing_screen = False
def snapshot(self):
self.scope.canvas.print_figure("scope_snapshot.svg")
def reset(self):
self.scope.max_value = self.max_value
self.scope.ax.set_ylim(0, self.scope.max_value)
self.scope.reset()
self.hist.reset()
self.min = self.max_value
self.max = 0
self.avg = 0
def key_press_event(self, widget, event):
if event.keyval == ord(' '):
self.freeze_screen(not self.refreshing_screen)
elif event.keyval in (ord('s'), ord('S')):
self.snapshot()
elif event.keyval in (ord('r'), ord('R')):
self.reset()
elif event.keyval in (ord('q'), ord('Q')):
gtk.main_quit()
class ftrace_window(gtk.Window):
(COL_FUNCTION, ) = range(1)
def __init__(self, trace, parent = None):
gtk.Window.__init__(self)
try:
self.set_screen(parent.get_screen())
except AttributeError:
self.connect('destroy', lambda *w: gtk.main_quit())
self.set_border_width(8)
self.set_default_size(350, 500)
self.set_title("ftrace")
vbox = gtk.VBox(False, 8)
self.add(vbox)
sw = gtk.ScrolledWindow()
sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
vbox.pack_start(sw, True, True)
store = gtk.ListStore(gobject.TYPE_STRING)
for entry in trace:
if entry[0] in [ "#", "\n" ] or entry[:4] == "vim:":
continue
iter = store.append()
store.set(iter, self.COL_FUNCTION, entry.strip())
treeview = gtk.TreeView(store)
treeview.set_rules_hint(True)
column = gtk.TreeViewColumn("Function", gtk.CellRendererText(),
text = self.COL_FUNCTION)
treeview.append_column(column)
sw.add(treeview)
self.show_all()
class cyclictestoscope(oscilloscope):
def __init__(self, max_value, snapshot_samples = 0, nr_samples_on_screen = 500,
delimiter = ':', field = 2, ylabel = "Latency",
geometry = None, scale = True, sample_multiplier = 1):
oscilloscope.__init__(self, self.get_sample,
title = "CyclictestoSCOPE",
nr_samples_on_screen = nr_samples_on_screen,
width = 900, max_value = max_value,
picker = self.scope_picker,
snapshot_samples = snapshot_samples,
ylabel = ylabel, geometry = geometry,
scale = scale)
self.connect("destroy", self.quit)
self.delimiter = delimiter
self.sample_multiplier = sample_multiplier
self.field = field
self.latency_tracer = os.access("/sys/kernel/debug/tracing/trace", os.R_OK)
if self.latency_tracer:
self.traces = [ None, ] * nr_samples_on_screen
def scope_picker(self, line, mouseevent):
if (not self.latency_tracer) or mouseevent.xdata is None:
return False, dict()
x = int(mouseevent.xdata)
if self.traces[x]:
fw = ftrace_window(self.traces[x], self)
return False, dict()
def get_sample(self):
fields = sys.stdin.readline().split(self.delimiter)
try:
sample = float(fields[self.field]) * self.sample_multiplier
except:
print "fields=%s, self.field=%s,self.delimiter=%s" % (fields, self.field, self.delimiter)
return None
if self.latency_tracer:
del self.traces[0]
if sample > self.avg:
print sample
try:
f = file("/sys/kernel/debug/tracing/trace")
trace = f.readlines()
f.close()
f = file("/sys/kernel/debug/tracing/tracing_max_latency", "w")
f.write("0\n")
f.close()
except:
trace = None
else:
print "-"
trace = None
self.traces.append(trace)
return sample
def run(self):
oscilloscope.run(self, sys.stdin.fileno())
def quit(self, x):
gtk.main_quit()