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 | ||