diff options
| author | Steven Rostedt <srostedt@redhat.com> | 2009-12-30 13:30:48 -0500 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2009-12-30 13:30:48 -0500 |
| commit | 708b9e195cbaebdbea5bfccb751c7fce0a25b9f7 (patch) | |
| tree | 9480a14c7d5112897f540c2a7ed42fab85ffc7b5 | |
| parent | 53336fb4d2d41f865557bb5ff0bc686db5e31793 (diff) | |
| parent | 5f85740ef7a9e7e61fdbd2583a1ff2b1001c4418 (diff) | |
Merge branch 'trace-cmd' into trace-view
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | ctracecmd.i | 9 | ||||
| -rwxr-xr-x | event-viewer.py | 272 | ||||
| -rw-r--r-- | tracecmd.py | 36 |
4 files changed, 310 insertions, 11 deletions
| @@ -119,10 +119,12 @@ plugin_mac80211.so: plugin_mac80211.o | |||
| 119 | 119 | ||
| 120 | 120 | ||
| 121 | .PHONY: python | 121 | .PHONY: python |
| 122 | python: $(TCMD_LIB_OBJS) trace-cmd.o trace-read.o | 122 | python: $(TCMD_LIB_OBJS) |
| 123 | swig -Wall -python -noproxy ctracecmd.i | 123 | swig -Wall -python -noproxy ctracecmd.i |
| 124 | #swig -Wall -python ctracecmd.i | ||
| 124 | gcc -fpic -c `python-config --includes` ctracecmd_wrap.c | 125 | gcc -fpic -c `python-config --includes` ctracecmd_wrap.c |
| 125 | $(CC) --shared $^ ctracecmd_wrap.o -o ctracecmd.so | 126 | $(CC) --shared $^ ctracecmd_wrap.o -o ctracecmd.so |
| 127 | #$(CC) --shared $^ ctracecmd_wrap.o -o _ctracecmd.so | ||
| 126 | 128 | ||
| 127 | 129 | ||
| 128 | .PHONY: force | 130 | .PHONY: force |
diff --git a/ctracecmd.i b/ctracecmd.i index a9d3ca5..f0b0eb7 100644 --- a/ctracecmd.i +++ b/ctracecmd.i | |||
| @@ -2,14 +2,23 @@ | |||
| 2 | %module ctracecmd | 2 | %module ctracecmd |
| 3 | %include typemaps.i | 3 | %include typemaps.i |
| 4 | 4 | ||
| 5 | /* return a (rec,cpu) tuple in python */ | ||
| 6 | extern struct record *tracecmd_read_at(struct tracecmd_input *handle, | ||
| 7 | unsigned long long offset, | ||
| 8 | int *OUTPUT); | ||
| 9 | |||
| 10 | |||
| 5 | %{ | 11 | %{ |
| 6 | #include "trace-cmd.h" | 12 | #include "trace-cmd.h" |
| 7 | %} | 13 | %} |
| 8 | 14 | ||
| 15 | |||
| 16 | /* return python longs from unsigned long long functions */ | ||
| 9 | %typemap(out) unsigned long long { | 17 | %typemap(out) unsigned long long { |
| 10 | $result = PyLong_FromUnsignedLongLong((unsigned long long) $1); | 18 | $result = PyLong_FromUnsignedLongLong((unsigned long long) $1); |
| 11 | } | 19 | } |
| 12 | 20 | ||
| 21 | |||
| 13 | %inline %{ | 22 | %inline %{ |
| 14 | PyObject *pevent_read_number_field_py(struct format_field *f, void *data) | 23 | PyObject *pevent_read_number_field_py(struct format_field *f, void *data) |
| 15 | { | 24 | { |
diff --git a/event-viewer.py b/event-viewer.py new file mode 100755 index 0000000..6f35642 --- /dev/null +++ b/event-viewer.py | |||
| @@ -0,0 +1,272 @@ | |||
| 1 | #!/usr/bin/env python | ||
| 2 | |||
| 3 | import getopt | ||
| 4 | from gobject import * | ||
| 5 | import gtk | ||
| 6 | from tracecmd import * | ||
| 7 | import time | ||
| 8 | |||
| 9 | app = None | ||
| 10 | data_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. | ||
| 14 | TS_COL_W = 150 | ||
| 15 | CPU_COL_W = 35 | ||
| 16 | EVENT_COL_W = 150 | ||
| 17 | PID_COL_W = 75 | ||
| 18 | COMM_COL_W = 250 | ||
| 19 | |||
| 20 | |||
| 21 | def 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 | |||
| 31 | class 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 = record_offset_get(rec) | ||
| 72 | ts = 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 | |||
| 152 | class 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.%d" % (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 | |||
| 226 | class 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 | |||
| 261 | if __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() | ||
diff --git a/tracecmd.py b/tracecmd.py index f9a2708..e7ea5f6 100644 --- a/tracecmd.py +++ b/tracecmd.py | |||
| @@ -32,15 +32,20 @@ TODO: consider a complete class hierarchy of ftrace events... | |||
| 32 | """ | 32 | """ |
| 33 | 33 | ||
| 34 | class Event(object): | 34 | class Event(object): |
| 35 | def __init__(self, trace, record): | 35 | def __init__(self, trace, record, cpu): |
| 36 | self.trace = trace | 36 | self.trace = trace |
| 37 | self.rec = record | 37 | self.rec = record |
| 38 | self.cpu = cpu | ||
| 38 | type = pevent_data_type(trace.pe, record) | 39 | type = pevent_data_type(trace.pe, record) |
| 39 | self.ec = pevent_data_event_from_type(trace.pe, type) | 40 | self.format = pevent_data_event_from_type(trace.pe, type) |
| 40 | 41 | ||
| 41 | def __str__(self): | 42 | def __str__(self): |
| 42 | return "%d.%d %s: pid=%d comm=%s type=%d" % \ | 43 | return "%d.%d CPU%d %s: pid=%d comm=%s type=%d" % \ |
| 43 | (self.ts/1000000000, self.ts%1000000000, self.name, self.num_field("common_pid"), self.comm, self.type) | 44 | (self.ts/1000000000, self.ts%1000000000, self.cpu, self.name, |
| 45 | self.num_field("common_pid"), self.comm, self.type) | ||
| 46 | |||
| 47 | def __del__(self): | ||
| 48 | free_record(self.rec); | ||
| 44 | 49 | ||
| 45 | 50 | ||
| 46 | # TODO: consider caching the results of the properties | 51 | # TODO: consider caching the results of the properties |
| @@ -50,7 +55,7 @@ class Event(object): | |||
| 50 | 55 | ||
| 51 | @property | 56 | @property |
| 52 | def name(self): | 57 | def name(self): |
| 53 | return event_name_get(self.ec) | 58 | return event_format_name_get(self.format) |
| 54 | 59 | ||
| 55 | @property | 60 | @property |
| 56 | def pid(self): | 61 | def pid(self): |
| @@ -65,7 +70,7 @@ class Event(object): | |||
| 65 | return pevent_data_type(self.trace.pe, self.rec) | 70 | return pevent_data_type(self.trace.pe, self.rec) |
| 66 | 71 | ||
| 67 | def num_field(self, name): | 72 | def num_field(self, name): |
| 68 | f = pevent_find_any_field(self.ec, name) | 73 | f = pevent_find_any_field(self.format, name) |
| 69 | val = pevent_read_number_field_py(f, record_data_get(self.rec)) | 74 | val = pevent_read_number_field_py(f, record_data_get(self.rec)) |
| 70 | return val | 75 | return val |
| 71 | 76 | ||
| @@ -82,9 +87,7 @@ class Trace(object): | |||
| 82 | self.pe = None | 87 | self.pe = None |
| 83 | 88 | ||
| 84 | try: | 89 | try: |
| 85 | file = open(filename) | 90 | self.handle = tracecmd_open(filename) |
| 86 | self.handle = tracecmd_open(file.fileno()) | ||
| 87 | print "self.handle: ", self.handle | ||
| 88 | #FIXME: check if these throw exceptions automatically or if we have | 91 | #FIXME: check if these throw exceptions automatically or if we have |
| 89 | # to check return codes manually | 92 | # to check return codes manually |
| 90 | tracecmd_read_headers(self.handle) | 93 | tracecmd_read_headers(self.handle) |
| @@ -100,9 +103,22 @@ class Trace(object): | |||
| 100 | def read_event(self, cpu): | 103 | def read_event(self, cpu): |
| 101 | rec = tracecmd_read_data(self.handle, cpu) | 104 | rec = tracecmd_read_data(self.handle, cpu) |
| 102 | if rec: | 105 | if rec: |
| 103 | return Event(self, rec) | 106 | #rec.acquire() |
| 107 | #rec.thisown = 1 | ||
| 108 | return Event(self, rec, cpu) | ||
| 104 | return None | 109 | return None |
| 105 | 110 | ||
| 111 | def read_event_at(self, offset): | ||
| 112 | res = tracecmd_read_at(self.handle, offset) | ||
| 113 | # SWIG only returns the CPU if the record is None for some reason | ||
| 114 | if isinstance(res, int): | ||
| 115 | return None | ||
| 116 | rec,cpu = res | ||
| 117 | #rec.acquire() | ||
| 118 | #rec.thisown = 1 | ||
| 119 | ev = Event(self, rec, cpu) | ||
| 120 | return ev | ||
| 121 | |||
| 106 | def peek_event(self, cpu): | 122 | def peek_event(self, cpu): |
| 107 | pass | 123 | pass |
| 108 | 124 | ||
