| #!/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 = pevent_record_offset_get(rec) |
| ts = pevent_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() |