273 lines
7.7 KiB
Python
Executable File
273 lines
7.7 KiB
Python
Executable File
#!/usr/bin/env python2
|
|
|
|
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 = tep_record_offset_get(rec)
|
|
ts = tep_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.%09d" % (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()
|