#!/usr/bin/env python
import getopt
from gobject import *
import gtk
from tracecmd import *
import time
app = None
data_func_cnt = 0
# In a "real" app these width should be determined at runtime testing max length
# strings in the current font.
TS_COL_W = 150
CPU_COL_W = 35
EVENT_COL_W = 150
PID_COL_W = 75
COMM_COL_W = 250
def timing(func):
def wrapper(*arg):
start = time.time()
ret = func(*arg)
end = time.time()
print '@%s took %0.3f s' % (func.func_name, (end-start))
return ret
return wrapper
class EventStore(gtk.GenericTreeModel):
class EventRef(object):
'''Inner class to build the trace event index'''
def __init__(self, index, timestamp, offset, cpu):
self.index = index
self.offset = offset
self.ts = timestamp
self.cpu = cpu
def __cmp__(self, other):
if self.ts < other.ts:
return -1
if self.ts > other.ts:
return 1
if self.offset < other.offset:
return -1
if self.offset > other.offset:
return 1
return 0
# The store only returns the record offset into the trace
# The view is responsible for looking up the Event with the offset
column_types = (long,)
@timing
def __init__(self, trace):
gtk.GenericTreeModel.__init__(self)
self.trace = trace
self.refs = []
self._load_trace()
self._sort()
self._reindex()
@timing
def _load_trace(self):
print "Building trace index..."
index = 0
for cpu in range(0, trace.cpus):
rec = tracecmd_read_data(self.trace.handle, cpu)
while rec:
offset = record_offset_get(rec)
ts = record_ts_get(rec)
self.refs.append(self.EventRef(index, ts, offset, cpu))
index = index + 1
rec = tracecmd_read_data(self.trace.handle, cpu)
print "Loaded %d events from trace" % (index)
@timing
def _sort(self):
self.refs.sort()
@timing
def _reindex(self):
for i in range(0, len(self.refs)):
self.refs[i].index = i
def on_get_flags(self):
return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST
def on_get_n_columns(self):
return len(self.column_types)
def on_get_column_type(self, col):
return self.column_types[col]
def on_get_iter(self, path):
return self.refs[path[0]]
def on_get_path(self, ref):
return ref.index
def on_get_value(self, ref, col):
'''
The Event record was getting deleted when passed back via this
method, now it just returns the ref itself. Use get_event() instead.
'''
if col == 0:
#return self.trace.read_event_at(ref.offset)
return ref
return None
def on_iter_next(self, ref):
try:
return self.refs[ref.index+1]
except IndexError:
return None
def on_iter_children(self, ref):
if ref:
return None
return self.refs[0]
def on_iter_has_child(self, ref):
return False
def on_iter_n_children(self, ref):
if ref:
return 0
return len(self.refs)
def on_iter_nth_child(self, ref, n):
if ref:
return None
try:
return self.refs[n]
except IndexError:
return None
def on_iter_parent(self, child):
return None
def get_event(self, iter):
'''This allocates a record which must be freed by the caller'''
try:
ref = self.refs[self.get_path(iter)[0]]
ev = self.trace.read_event_at(ref.offset)
return ev
except IndexError:
return None
class EventView(gtk.TreeView):
def __init__(self, model):
gtk.TreeView.__init__(self, model)
self.set_fixed_height_mode(True)
ts_col = gtk.TreeViewColumn("Time (s)")
ts_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
ts_col.set_fixed_width(TS_COL_W)
ts_cell = gtk.CellRendererText()
ts_col.pack_start(ts_cell, False)
ts_col.set_cell_data_func(ts_cell, self.data_func, "ts")
self.append_column(ts_col)
cpu_col = gtk.TreeViewColumn("CPU")
cpu_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
cpu_col.set_fixed_width(CPU_COL_W)
cpu_cell = gtk.CellRendererText()
cpu_col.pack_start(cpu_cell, False)
cpu_col.set_cell_data_func(cpu_cell, self.data_func, "cpu")
self.append_column(cpu_col)
event_col = gtk.TreeViewColumn("Event")
event_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
event_col.set_fixed_width(EVENT_COL_W)
event_cell = gtk.CellRendererText()
event_col.pack_start(event_cell, False)
event_col.set_cell_data_func(event_cell, self.data_func, "event")
self.append_column(event_col)
pid_col = gtk.TreeViewColumn("PID")
pid_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
pid_col.set_fixed_width(PID_COL_W)
pid_cell = gtk.CellRendererText()
pid_col.pack_start(pid_cell, False)
pid_col.set_cell_data_func(pid_cell, self.data_func, "pid")
self.append_column(pid_col)
comm_col = gtk.TreeViewColumn("Comm")
comm_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
comm_col.set_fixed_width(COMM_COL_W)
comm_cell = gtk.CellRendererText()
comm_col.pack_start(comm_cell, False)
comm_col.set_cell_data_func(comm_cell, self.data_func, "comm")
self.append_column(comm_col)
def data_func(self, col, cell, model, iter, data):
global app, data_func_cnt
ev = model.get_event(iter)
#ev = model.get_value(iter, 0)
if not ev:
return False
if data == "ts":
cell.set_property("markup", "%d.%d" % (ev.ts/1000000000,
ev.ts%1000000000))
data_func_cnt = data_func_cnt + 1
if app:
app.inc_data_func()
elif data == "cpu":
cell.set_property("markup", ev.cpu)
elif data == "event":
cell.set_property("markup", ev.name)
elif data == "pid":
cell.set_property("markup", ev.pid)
elif data == "comm":
cell.set_property("markup", ev.comm)
else:
print "Unknown Column:", data
return False
return True
class EventViewerApp(gtk.Window):
def __init__(self, trace):
gtk.Window.__init__(self)
self.set_size_request(650, 400)
self.set_position(gtk.WIN_POS_CENTER)
self.connect("destroy", gtk.main_quit)
self.set_title("Event Viewer")
store = EventStore(trace)
view = EventView(store)
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
sw.add(view)
# track how often the treeview data_func is called
self.data_func_label = gtk.Label("0")
hbox = gtk.HBox()
hbox.pack_start(gtk.Label("TS Data Func Calls:"), False, False)
hbox.pack_start(self.data_func_label, False, False)
vbox = gtk.VBox()
vbox.pack_start(hbox, False)
vbox.pack_end(sw)
self.add(vbox)
self.show_all()
def inc_data_func(self):
global data_func_cnt
self.data_func_label.set_text(str(data_func_cnt))
if __name__ == "__main__":
if len(sys.argv) >=2:
filename = sys.argv[1]
else:
filename = "trace.dat"
print "Initializing trace..."
trace = Trace(filename)
print "Initializing app..."
app = EventViewerApp(trace)
print "Go!"
gtk.main()