aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarren Hart <dvhltc@us.ibm.com>2009-12-29 01:25:13 -0500
committerDarren Hart <dvhltc@us.ibm.com>2009-12-30 12:22:41 -0500
commit5f85740ef7a9e7e61fdbd2583a1ff2b1001c4418 (patch)
treee405a2e62a5d5e567b001dd3a5789012998b5681
parent9703124e36ad78a1484138dbe010d587aed5a60b (diff)
trace-cmd: Python EventStore and record memory management
Add a python EventStore instead of a generic gtk.ListStore. Load time is reduced 50% although still 15x slower than the C version. Add the proper freeing of the record to the Event destructor rather than setting ownership of the pointer to Python. The latter causes swig to call free() on the record rather than free_record() which does some intelligent things regarding the mmap. Signed-off-by: Darren Hart <dvhltc@us.ibm.com>
-rw-r--r--Makefile2
-rwxr-xr-xevent-viewer.py172
-rw-r--r--tracecmd.py11
3 files changed, 160 insertions, 25 deletions
diff --git a/Makefile b/Makefile
index 32f85f1..1bcc153 100644
--- a/Makefile
+++ b/Makefile
@@ -87,8 +87,10 @@ plugin_mac80211.so: plugin_mac80211.o
87.PHONY: python 87.PHONY: python
88python: $(TCMD_LIB_OBJS) 88python: $(TCMD_LIB_OBJS)
89 swig -Wall -python -noproxy ctracecmd.i 89 swig -Wall -python -noproxy ctracecmd.i
90 #swig -Wall -python ctracecmd.i
90 gcc -fpic -c `python-config --includes` ctracecmd_wrap.c 91 gcc -fpic -c `python-config --includes` ctracecmd_wrap.c
91 $(CC) --shared $^ ctracecmd_wrap.o -o ctracecmd.so 92 $(CC) --shared $^ ctracecmd_wrap.o -o ctracecmd.so
93 #$(CC) --shared $^ ctracecmd_wrap.o -o _ctracecmd.so
92 94
93 95
94.PHONY: force 96.PHONY: force
diff --git a/event-viewer.py b/event-viewer.py
index a8ccd5b..6f35642 100755
--- a/event-viewer.py
+++ b/event-viewer.py
@@ -1,8 +1,10 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2 2
3import getopt
3from gobject import * 4from gobject import *
4import gtk 5import gtk
5from tracecmd import * 6from tracecmd import *
7import time
6 8
7app = None 9app = None
8data_func_cnt = 0 10data_func_cnt = 0
@@ -15,21 +17,136 @@ EVENT_COL_W = 150
15PID_COL_W = 75 17PID_COL_W = 75
16COMM_COL_W = 250 18COMM_COL_W = 250
17 19
18class EventStore(gtk.ListStore): 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
19 def __init__(self, trace): 56 def __init__(self, trace):
20 gtk.ListStore.__init__(self, gobject.TYPE_PYOBJECT) 57 gtk.GenericTreeModel.__init__(self)
21 self.trace = trace 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
22 for cpu in range(0, trace.cpus): 68 for cpu in range(0, trace.cpus):
23 ev = trace.read_event(cpu) 69 rec = tracecmd_read_data(self.trace.handle, cpu)
24 while ev: 70 while rec:
25 # store the record offset into the trace file 71 offset = record_offset_get(rec)
26 self.append([record_offset_get(ev.rec)]) 72 ts = record_ts_get(rec)
27 ev = trace.read_event(cpu) 73 self.refs.append(self.EventRef(index, ts, offset, cpu))
28 print "Loaded %d events across %d cpus" % (len(self), trace.cpus) 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
29 141
30 def get_event(self, iter): 142 def get_event(self, iter):
31 offset = self.get_value(iter, 0) 143 '''This allocates a record which must be freed by the caller'''
32 return self.trace.read_event_at(offset) 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
33 150
34 151
35class EventView(gtk.TreeView): 152class EventView(gtk.TreeView):
@@ -81,29 +198,29 @@ class EventView(gtk.TreeView):
81 global app, data_func_cnt 198 global app, data_func_cnt
82 199
83 ev = model.get_event(iter) 200 ev = model.get_event(iter)
201 #ev = model.get_value(iter, 0)
84 if not ev: 202 if not ev:
85 return False 203 return False
204
86 if data == "ts": 205 if data == "ts":
87 cell.set_property("markup", "%d.%d" % (ev.ts/1000000000, 206 cell.set_property("markup", "%d.%d" % (ev.ts/1000000000,
88 ev.ts%1000000000)) 207 ev.ts%1000000000))
89 data_func_cnt = data_func_cnt + 1 208 data_func_cnt = data_func_cnt + 1
90 if app: 209 if app:
91 app.inc_data_func() 210 app.inc_data_func()
92 return True 211 elif data == "cpu":
93 if data == "cpu":
94 cell.set_property("markup", ev.cpu) 212 cell.set_property("markup", ev.cpu)
95 return True 213 elif data == "event":
96 if data == "event":
97 cell.set_property("markup", ev.name) 214 cell.set_property("markup", ev.name)
98 return True 215 elif data == "pid":
99 if data == "pid":
100 cell.set_property("markup", ev.pid) 216 cell.set_property("markup", ev.pid)
101 return True 217 elif data == "comm":
102 if data == "comm":
103 cell.set_property("markup", ev.comm) 218 cell.set_property("markup", ev.comm)
104 return True 219 else:
220 print "Unknown Column:", data
221 return False
105 222
106 return False 223 return True
107 224
108 225
109class EventViewerApp(gtk.Window): 226class EventViewerApp(gtk.Window):
@@ -116,9 +233,8 @@ class EventViewerApp(gtk.Window):
116 self.connect("destroy", gtk.main_quit) 233 self.connect("destroy", gtk.main_quit)
117 self.set_title("Event Viewer") 234 self.set_title("Event Viewer")
118 235
119 236 store = EventStore(trace)
120 es = EventStore(trace) 237 view = EventView(store)
121 view = EventView(es)
122 238
123 sw = gtk.ScrolledWindow() 239 sw = gtk.ScrolledWindow()
124 sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) 240 sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
@@ -143,6 +259,14 @@ class EventViewerApp(gtk.Window):
143 259
144 260
145if __name__ == "__main__": 261if __name__ == "__main__":
146 trace = Trace("trace.dat") 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..."
147 app = EventViewerApp(trace) 270 app = EventViewerApp(trace)
271 print "Go!"
148 gtk.main() 272 gtk.main()
diff --git a/tracecmd.py b/tracecmd.py
index fdcda76..e7ea5f6 100644
--- a/tracecmd.py
+++ b/tracecmd.py
@@ -44,6 +44,9 @@ class Event(object):
44 (self.ts/1000000000, self.ts%1000000000, self.cpu, self.name, 44 (self.ts/1000000000, self.ts%1000000000, self.cpu, self.name,
45 self.num_field("common_pid"), self.comm, self.type) 45 self.num_field("common_pid"), self.comm, self.type)
46 46
47 def __del__(self):
48 free_record(self.rec);
49
47 50
48 # TODO: consider caching the results of the properties 51 # TODO: consider caching the results of the properties
49 @property 52 @property
@@ -100,6 +103,8 @@ 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:
106 #rec.acquire()
107 #rec.thisown = 1
103 return Event(self, rec, cpu) 108 return Event(self, rec, cpu)
104 return None 109 return None
105 110
@@ -108,7 +113,11 @@ class Trace(object):
108 # SWIG only returns the CPU if the record is None for some reason 113 # SWIG only returns the CPU if the record is None for some reason
109 if isinstance(res, int): 114 if isinstance(res, int):
110 return None 115 return None
111 return Event(self, res[0], res[1]) 116 rec,cpu = res
117 #rec.acquire()
118 #rec.thisown = 1
119 ev = Event(self, rec, cpu)
120 return ev
112 121
113 def peek_event(self, cpu): 122 def peek_event(self, cpu):
114 pass 123 pass