1#!/usr/bin/env python2 2 3import getopt 4from gobject import * 5import gtk 6from tracecmd import * 7import time 8 9app = None 10data_func_cnt = 0 11 12# In a "real" app these width should be determined at runtime testing max length 13# strings in the current font. 14TS_COL_W = 150 15CPU_COL_W = 35 16EVENT_COL_W = 150 17PID_COL_W = 75 18COMM_COL_W = 250 19 20 21def timing(func): 22 def wrapper(*arg): 23 start = time.time() 24 ret = func(*arg) 25 end = time.time() 26 print('@%s took %0.3f s' % (func.func_name, (end-start))) 27 return ret 28 return wrapper 29 30 31class EventStore(gtk.GenericTreeModel): 32 class EventRef(object): 33 '''Inner class to build the trace event index''' 34 def __init__(self, index, timestamp, offset, cpu): 35 self.index = index 36 self.offset = offset 37 self.ts = timestamp 38 self.cpu = cpu 39 40 def __cmp__(self, other): 41 if self.ts < other.ts: 42 return -1 43 if self.ts > other.ts: 44 return 1 45 if self.offset < other.offset: 46 return -1 47 if self.offset > other.offset: 48 return 1 49 return 0 50 51 # The store only returns the record offset into the trace 52 # The view is responsible for looking up the Event with the offset 53 column_types = (long,) 54 55 @timing 56 def __init__(self, trace): 57 gtk.GenericTreeModel.__init__(self) 58 self.trace = trace 59 self.refs = [] 60 self._load_trace() 61 self._sort() 62 self._reindex() 63 64 @timing 65 def _load_trace(self): 66 print("Building trace index...") 67 index = 0 68 for cpu in range(0, trace.cpus): 69 rec = tracecmd_read_data(self.trace._handle, cpu) 70 while rec: 71 offset = tep_record_offset_get(rec) 72 ts = tep_record_ts_get(rec) 73 self.refs.append(self.EventRef(index, ts, offset, cpu)) 74 index = index + 1 75 rec = tracecmd_read_data(self.trace._handle, cpu) 76 print("Loaded %d events from trace" % (index)) 77 78 @timing 79 def _sort(self): 80 self.refs.sort() 81 82 @timing 83 def _reindex(self): 84 for i in range(0, len(self.refs)): 85 self.refs[i].index = i 86 87 def on_get_flags(self): 88 return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST 89 90 def on_get_n_columns(self): 91 return len(self.column_types) 92 93 def on_get_column_type(self, col): 94 return self.column_types[col] 95 96 def on_get_iter(self, path): 97 return self.refs[path[0]] 98 99 def on_get_path(self, ref): 100 return ref.index 101 102 def on_get_value(self, ref, col): 103 ''' 104 The Event record was getting deleted when passed back via this 105 method, now it just returns the ref itself. Use get_event() instead. 106 ''' 107 if col == 0: 108 #return self.trace.read_event_at(ref.offset) 109 return ref 110 return None 111 112 def on_iter_next(self, ref): 113 try: 114 return self.refs[ref.index+1] 115 except IndexError: 116 return None 117 118 def on_iter_children(self, ref): 119 if ref: 120 return None 121 return self.refs[0] 122 123 def on_iter_has_child(self, ref): 124 return False 125 126 def on_iter_n_children(self, ref): 127 if ref: 128 return 0 129 return len(self.refs) 130 131 def on_iter_nth_child(self, ref, n): 132 if ref: 133 return None 134 try: 135 return self.refs[n] 136 except IndexError: 137 return None 138 139 def on_iter_parent(self, child): 140 return None 141 142 def get_event(self, iter): 143 '''This allocates a record which must be freed by the caller''' 144 try: 145 ref = self.refs[self.get_path(iter)[0]] 146 ev = self.trace.read_event_at(ref.offset) 147 return ev 148 except IndexError: 149 return None 150 151 152class EventView(gtk.TreeView): 153 def __init__(self, model): 154 gtk.TreeView.__init__(self, model) 155 self.set_fixed_height_mode(True) 156 157 ts_col = gtk.TreeViewColumn("Time (s)") 158 ts_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) 159 ts_col.set_fixed_width(TS_COL_W) 160 ts_cell = gtk.CellRendererText() 161 ts_col.pack_start(ts_cell, False) 162 ts_col.set_cell_data_func(ts_cell, self.data_func, "ts") 163 self.append_column(ts_col) 164 165 cpu_col = gtk.TreeViewColumn("CPU") 166 cpu_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) 167 cpu_col.set_fixed_width(CPU_COL_W) 168 cpu_cell = gtk.CellRendererText() 169 cpu_col.pack_start(cpu_cell, False) 170 cpu_col.set_cell_data_func(cpu_cell, self.data_func, "cpu") 171 self.append_column(cpu_col) 172 173 event_col = gtk.TreeViewColumn("Event") 174 event_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) 175 event_col.set_fixed_width(EVENT_COL_W) 176 event_cell = gtk.CellRendererText() 177 event_col.pack_start(event_cell, False) 178 event_col.set_cell_data_func(event_cell, self.data_func, "event") 179 self.append_column(event_col) 180 181 pid_col = gtk.TreeViewColumn("PID") 182 pid_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) 183 pid_col.set_fixed_width(PID_COL_W) 184 pid_cell = gtk.CellRendererText() 185 pid_col.pack_start(pid_cell, False) 186 pid_col.set_cell_data_func(pid_cell, self.data_func, "pid") 187 self.append_column(pid_col) 188 189 comm_col = gtk.TreeViewColumn("Comm") 190 comm_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) 191 comm_col.set_fixed_width(COMM_COL_W) 192 comm_cell = gtk.CellRendererText() 193 comm_col.pack_start(comm_cell, False) 194 comm_col.set_cell_data_func(comm_cell, self.data_func, "comm") 195 self.append_column(comm_col) 196 197 def data_func(self, col, cell, model, iter, data): 198 global app, data_func_cnt 199 200 ev = model.get_event(iter) 201 #ev = model.get_value(iter, 0) 202 if not ev: 203 return False 204 205 if data == "ts": 206 cell.set_property("markup", "%d.%09d" % (ev.ts/1000000000, 207 ev.ts%1000000000)) 208 data_func_cnt = data_func_cnt + 1 209 if app: 210 app.inc_data_func() 211 elif data == "cpu": 212 cell.set_property("markup", ev.cpu) 213 elif data == "event": 214 cell.set_property("markup", ev.name) 215 elif data == "pid": 216 cell.set_property("markup", ev.pid) 217 elif data == "comm": 218 cell.set_property("markup", ev.comm) 219 else: 220 print("Unknown Column:", data) 221 return False 222 223 return True 224 225 226class EventViewerApp(gtk.Window): 227 def __init__(self, trace): 228 gtk.Window.__init__(self) 229 230 self.set_size_request(650, 400) 231 self.set_position(gtk.WIN_POS_CENTER) 232 233 self.connect("destroy", gtk.main_quit) 234 self.set_title("Event Viewer") 235 236 store = EventStore(trace) 237 view = EventView(store) 238 239 sw = gtk.ScrolledWindow() 240 sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 241 sw.add(view) 242 243 # track how often the treeview data_func is called 244 self.data_func_label = gtk.Label("0") 245 hbox = gtk.HBox() 246 hbox.pack_start(gtk.Label("TS Data Func Calls:"), False, False) 247 hbox.pack_start(self.data_func_label, False, False) 248 249 vbox = gtk.VBox() 250 vbox.pack_start(hbox, False) 251 vbox.pack_end(sw) 252 253 self.add(vbox) 254 self.show_all() 255 256 def inc_data_func(self): 257 global data_func_cnt 258 self.data_func_label.set_text(str(data_func_cnt)) 259 260 261if __name__ == "__main__": 262 if len(sys.argv) >=2: 263 filename = sys.argv[1] 264 else: 265 filename = "trace.dat" 266 267 print("Initializing trace...") 268 trace = Trace(filename) 269 print("Initializing app...") 270 app = EventViewerApp(trace) 271 print("Go!") 272 gtk.main() 273