aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2010-05-25 10:26:12 -0400
committerSteven Rostedt <rostedt@goodmis.org>2010-05-25 10:26:12 -0400
commit6bf94a1fa0441c60142cd3aa9c6e0c42cd7ce402 (patch)
tree93be84b1b28e34db8d580984634bbcf890244265
parentf2bda4634ff4deda253ab91f1b91c8006e3f95c4 (diff)
parent339321b326d241dcf6490ed910e8122486e29cd6 (diff)
Merge branch 'python2' of http://git.sipsolutions.net/trace-cmd into trace-cmd
-rw-r--r--Documentation/README.PythonPlugin127
-rw-r--r--Makefile23
-rw-r--r--ctracecmd.i159
-rw-r--r--parse-events.h2
-rw-r--r--plugin_python.c136
-rw-r--r--trace-ftrace.c9
-rw-r--r--trace-input.c43
-rw-r--r--trace-plot-cpu.c2
-rw-r--r--trace-plot-task.c1
-rw-r--r--trace-util.c2
-rw-r--r--tracecmd.py167
11 files changed, 592 insertions, 79 deletions
diff --git a/Documentation/README.PythonPlugin b/Documentation/README.PythonPlugin
new file mode 100644
index 0000000..3de0564
--- /dev/null
+++ b/Documentation/README.PythonPlugin
@@ -0,0 +1,127 @@
1 PYTHON PLUGIN DOCUMENTATION
2=============================
3
4With the python plugin (make python-plugin) you can now
5write plugins in python. The API exported by the python
6plugin itself (written in C) allows you to access most
7information about a record from python.
8
9To write a python plugin, put a new .py file into a new
10~/.trace-cmd/python/ directory.
11
12The most basic python plugin is this:
13
14--- %< ---
15def register(pevent):
16 pass
17--- >% ---
18
19which obviously does nothing at all.
20
21To register a callback, use the pevent.register_event_handler
22function:
23
24--- %< ---
25import tracecmd
26
27def my_event_handler(trace_seq, event):
28 pass
29
30def register(pevent):
31 pevent.register_event_handler("subsys", "event_name",
32 my_event_handler)
33--- >% ---
34
35
36There are four object types that you get, described below.
37
38 tracecmd.PEvent
39-----------------
40
41This is the class of the 'pevent' object above,
42you get one of those via your register callback.
43It has one method and one property:
44 * register_event_handler() - example above, to register
45 an event handler function
46 * file_endian - either '<' or '>' indicating
47 which endianness the file has,
48 to be used with struct.unpack()
49
50 tracecmd.TraceSeq
51-------------------
52
53This is the class of the 'trace_seq' parameter to your callback
54function. It has only one method, puts(), to put data into the
55buffer. Formatting must be done in python.
56
57 tracecmd.Event
58----------------------
59
60This is the class of the 'event' parameter to your callback
61function. Note that it doesn't just contain the format, but
62also the event data. As such, you can do much with this, and
63this is what you'll usually use. Each instance of this allows
64access to record items via the dict protocol, and you can get
65the items via its keys() methods. So for example, your
66callback could be
67
68--- %< ---
69def my_callback(trace_seq, event):
70 for fieldname in event.keys():
71 field = event[fieldname]
72--- >% ---
73
74Each field returned from the dict protocol is an instance of
75the next (and last) class:
76
77 tracecmd.Field
78----------------------
79
80This is an instance of a field, including its data. It affords
81numerous use cases and is what you'll be using most.
82
83 * If this is an integer field, i.e. 1, 2, 4 or 8 bytes long,
84 you can convert it to the number contained, according to
85 the file's endianness, by simply casting it to a long:
86
87 field = event['myint']
88 value = long(field)
89
90 * You can access the field's data, as field.data, and if the
91 data is really a "__data_loc" type that will be resolved
92 automatically. (If you don't know what this means, don't
93 worry about it and just use field.data)
94
95
96This is it. It's pretty simple. A fully-featured plugin could
97look like this:
98
99--- %< ---
100def my_event_handler(trace_seq, event):
101 trace_seq.puts("myev: %u", long(event['myfield']))
102
103def register(pevent):
104 pevent.register_event_handler("subsys", "event_name",
105 my_event_handler)
106--- >% ---
107
108
109 Tips and tricks
110-----------------
111
112Be familiar with the struct module and use it, always
113checking endianness and potentially using pevent.file_endian.
114
115
116If you need access to pevent in your callbacks, simply
117pass it in yourself:
118
119--- %< ---
120def my_event_handler(pevent, trace_seq, event):
121 pass
122
123def register(pevent):
124 pevent.register_event_handler("subsys", "event_name",
125 lambda *args: my_event_handler(pevent, *args)
126 )
127--- >% ---
diff --git a/Makefile b/Makefile
index 239c05a..a4a89ed 100644
--- a/Makefile
+++ b/Makefile
@@ -439,12 +439,14 @@ clean:
439##### PYTHON STUFF ##### 439##### PYTHON STUFF #####
440 440
441PYTHON_INCLUDES = `python-config --includes` 441PYTHON_INCLUDES = `python-config --includes`
442PYTHON_LDFLAGS = `python-config --ldflags` \
443 $(shell python -c "import distutils.sysconfig; print distutils.sysconfig.get_config_var('LINKFORSHARED')")
442PYGTK_CFLAGS = `pkg-config --cflags pygtk-2.0` 444PYGTK_CFLAGS = `pkg-config --cflags pygtk-2.0`
443 445
444ctracecmd.so: $(TCMD_LIB_OBJS) 446ctracecmd.so: $(TCMD_LIB_OBJS) ctracecmd.i
445 swig -Wall -python -noproxy ctracecmd.i 447 swig -Wall -python -noproxy ctracecmd.i
446 gcc -fpic -c $(PYTHON_INCLUDES) ctracecmd_wrap.c 448 gcc -fpic -c $(PYTHON_INCLUDES) ctracecmd_wrap.c
447 $(CC) --shared $^ ctracecmd_wrap.o -o ctracecmd.so 449 $(CC) --shared $(TCMD_LIB_OBJS) ctracecmd_wrap.o -o ctracecmd.so
448 450
449ctracecmdgui.so: $(TRACE_VIEW_OBJS) $(LIB_FILE) 451ctracecmdgui.so: $(TRACE_VIEW_OBJS) $(LIB_FILE)
450 swig -Wall -python -noproxy ctracecmdgui.i 452 swig -Wall -python -noproxy ctracecmdgui.i
@@ -457,6 +459,23 @@ python: ctracecmd.so
457PHONY += python-gui 459PHONY += python-gui
458python-gui: ctracecmd.so ctracecmdgui.so 460python-gui: ctracecmd.so ctracecmdgui.so
459 461
462PHONY += python-plugin
463python-plugin: plugin_python.so python
464
465do_compile_python_plugin_obj = \
466 ($(print_plugin_obj_compile) \
467 $(CC) -c $(CFLAGS) $(PYTHON_INCLUDES) -fPIC -o $@ $<)
468
469do_python_plugin_build = \
470 ($(print_plugin_build) \
471 $(CC) -shared $(PYTHON_LDFLAGS) -o $@ $<)
472
473plugin_python.o: %.o : $(src)/%.c
474 $(Q)$(do_compile_python_plugin_obj)
475
476plugin_python.so: %.so: %.o
477 $(Q)$(do_python_plugin_build)
478
460endif # skip-makefile 479endif # skip-makefile
461 480
462PHONY += force 481PHONY += force
diff --git a/ctracecmd.i b/ctracecmd.i
index 51e98d5..a0baa3c 100644
--- a/ctracecmd.i
+++ b/ctracecmd.i
@@ -3,13 +3,14 @@
3%include "typemaps.i" 3%include "typemaps.i"
4%include "constraints.i" 4%include "constraints.i"
5 5
6%nodefaultctor record;
7%nodefaultdtor record;
8
6%apply Pointer NONNULL { struct tracecmd_input *handle }; 9%apply Pointer NONNULL { struct tracecmd_input *handle };
7%apply Pointer NONNULL { struct pevent *pevent }; 10%apply Pointer NONNULL { struct pevent *pevent };
8 11%apply Pointer NONNULL { struct format_field * };
9/* return a (rec,cpu) tuple in python */ 12%apply unsigned long long *OUTPUT {unsigned long long *}
10extern struct record *tracecmd_read_at(struct tracecmd_input *handle, 13%apply int *OUTPUT {int *}
11 unsigned long long offset,
12 int *OUTPUT);
13 14
14 15
15%{ 16%{
@@ -17,26 +18,150 @@ extern struct record *tracecmd_read_at(struct tracecmd_input *handle,
17%} 18%}
18 19
19 20
20/* return python longs from unsigned long long functions */ 21%typemap(in) PyObject *pyfunc {
21%typemap(out) unsigned long long { 22 if (!PyCallable_Check($input)) {
22$result = PyLong_FromUnsignedLongLong((unsigned long long) $1); 23 PyErr_SetString(PyExc_TypeError, "Need a callable object!");
24 return NULL;
25 }
26 $1 = $input;
23} 27}
24 28
29%ignore python_callback;
25 30
26%inline %{ 31%inline %{
27PyObject *pevent_read_number_field_py(struct format_field *f, void *data) 32static int python_callback(struct trace_seq *s,
33 struct record *record,
34 struct event_format *event,
35 void *context);
36
37PyObject *convert_pevent(unsigned long pevent)
38{
39 void *pev = (void *)pevent;
40 return SWIG_NewPointerObj(SWIG_as_voidptr(pev), SWIGTYPE_p_pevent, 0);
41}
42
43void py_pevent_register_event_handler(struct pevent *pevent, int id,
44 char *subsys, char *evname,
45 PyObject *pyfunc)
28{ 46{
29 unsigned long long val; 47 Py_INCREF(pyfunc);
30 int ret; 48 pevent_register_event_handler(pevent, id, subsys, evname,
31 49 python_callback, pyfunc);
32 ret = pevent_read_number_field(f, data, &val); 50}
33 if (ret) 51
34 Py_RETURN_NONE; 52static PyObject *py_field_get_data(struct format_field *f, struct record *r)
35 else 53{
36 return PyLong_FromUnsignedLongLong(val); 54 if (!strncmp(f->type, "__data_loc ", 11)) {
55 unsigned long long val;
56 int len, offset;
57
58 if (pevent_read_number_field(f, r->data, &val)) {
59 PyErr_SetString(PyExc_TypeError,
60 "Field is not a valid number");
61 return NULL;
62 }
63
64 /*
65 * The actual length of the dynamic array is stored
66 * in the top half of the field, and the offset
67 * is in the bottom half of the 32 bit field.
68 */
69 offset = val & 0xffff;
70 len = val >> 16;
71
72 return PyBuffer_FromMemory((char *)r->data + offset, len);
73 }
74
75 return PyBuffer_FromMemory((char *)r->data + f->offset, f->size);
76}
77
78static PyObject *py_field_get_str(struct format_field *f, struct record *r)
79{
80 if (!strncmp(f->type, "__data_loc ", 11)) {
81 unsigned long long val;
82 int offset;
83
84 if (pevent_read_number_field(f, r->data, &val)) {
85 PyErr_SetString(PyExc_TypeError,
86 "Field is not a valid number");
87 return NULL;
88 }
89
90 /*
91 * The actual length of the dynamic array is stored
92 * in the top half of the field, and the offset
93 * is in the bottom half of the 32 bit field.
94 */
95 offset = val & 0xffff;
96
97 return PyString_FromString((char *)r->data + offset);
98 }
99
100 return PyString_FromStringAndSize((char *)r->data + f->offset,
101 strnlen((char *)r->data + f->offset, f->size));
102}
103
104static PyObject *py_format_get_keys(struct event_format *ef)
105{
106 PyObject *list;
107 struct format_field *f;
108
109 list = PyList_New(0);
110
111 for (f = ef->format.fields; f; f = f->next) {
112 if (PyList_Append(list, PyString_FromString(f->name))) {
113 Py_DECREF(list);
114 return NULL;
115 }
116 }
117
118 return list;
119}
120%}
121
122
123%wrapper %{
124static int python_callback(struct trace_seq *s,
125 struct record *record,
126 struct event_format *event,
127 void *context)
128{
129 PyObject *arglist, *result;
130 int r = 0;
131
132 record->ref_count++;
133
134 arglist = Py_BuildValue("(OOO)",
135 SWIG_NewPointerObj(SWIG_as_voidptr(s),
136 SWIGTYPE_p_trace_seq, 0),
137 SWIG_NewPointerObj(SWIG_as_voidptr(record),
138 SWIGTYPE_p_record, 0),
139 SWIG_NewPointerObj(SWIG_as_voidptr(event),
140 SWIGTYPE_p_event_format, 0));
141
142 result = PyEval_CallObject(context, arglist);
143 Py_XDECREF(arglist);
144 if (result && result != Py_None) {
145 if (!PyInt_Check(result)) {
146 PyErr_SetString(PyExc_TypeError,
147 "callback must return int");
148 PyErr_Print();
149 Py_XDECREF(result);
150 return 0;
151 }
152 r = PyInt_AS_LONG(result);
153 } else if (result == Py_None)
154 r = 0;
155 else
156 PyErr_Print();
157
158 Py_XDECREF(result);
159
160 return r;
37} 161}
38%} 162%}
39 163
164
40%ignore trace_seq_vprintf; 165%ignore trace_seq_vprintf;
41 166
42/* SWIG can't grok these, define them to nothing */ 167/* SWIG can't grok these, define them to nothing */
diff --git a/parse-events.h b/parse-events.h
index be28245..400bc4c 100644
--- a/parse-events.h
+++ b/parse-events.h
@@ -43,7 +43,7 @@ struct record {
43 int size; /* size of data */ 43 int size; /* size of data */
44 void *data; 44 void *data;
45 int cpu; 45 int cpu;
46 int locked; /* Do not allow freeing */ 46 int ref_count;
47 void *private; 47 void *private;
48}; 48};
49 49
diff --git a/plugin_python.c b/plugin_python.c
new file mode 100644
index 0000000..164e774
--- /dev/null
+++ b/plugin_python.c
@@ -0,0 +1,136 @@
1#include <Python.h>
2#ifndef _GNU_SOURCE
3#define _GNU_SOURCE
4#endif
5#include <stdio.h>
6#include <dirent.h>
7#include <fnmatch.h>
8#include "trace-cmd.h"
9
10static const char pyload[] =
11"import imp, tracecmd, ctracecmd\n"
12"fn = r'%s'\n"
13"file = open(fn, 'r')\n"
14"try:\n"
15" module = imp.load_source('%s', fn, file)\n"
16" module.register(tracecmd.PEvent(ctracecmd.convert_pevent(pevent)))\n"
17"finally:\n"
18" file.close()\n";
19
20static void load_plugin(PyObject *globals, char *path, const char *name)
21{
22 int len = strlen(path) + strlen(name) + 2;
23 int nlen = strlen(name) + 1;
24 char *full = malloc(len);
25 char *n = malloc(nlen);
26 char *load;
27 PyObject *res;
28
29 if (!full || !n)
30 return;
31
32 strcpy(full, path);
33 strcat(full, "/");
34 strcat(full, name);
35
36 strcpy(n, name);
37 n[nlen - 4] = '\0';
38
39 asprintf(&load, pyload, full, n);
40 if (!load)
41 return;
42
43 res = PyRun_String(load, Py_file_input, globals, globals);
44 if (!res) {
45 fprintf(stderr, "failed loading %s\n", full);
46 PyErr_Print();
47 } else
48 Py_DECREF(res);
49
50 free(load);
51}
52
53static int load_plugins(PyObject *globals, char *path)
54{
55 struct dirent *dent;
56 struct stat st;
57 DIR *dir;
58 int ret;
59
60 ret = stat(path, &st);
61 if (ret < 0)
62 return -1;
63
64 if (!S_ISDIR(st.st_mode))
65 return -1;
66
67 dir = opendir(path);
68 if (!dir)
69 return -1;
70
71 while ((dent = readdir(dir))) {
72 const char *name = dent->d_name;
73
74 if (fnmatch("*.py", name, FNM_PERIOD))
75 continue;
76
77 load_plugin(globals, path, name);
78 }
79
80 closedir(dir);
81
82 return 0;
83}
84
85#define LOCAL_PLUGIN_DIR ".trace-cmd/python"
86
87int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
88{
89 char *home;
90 char *path;
91 int ret;
92 PyObject *globals, *m, *py_pevent, *str;
93
94 home = getenv("HOME");
95 if (!home)
96 return 0;
97
98 Py_Initialize();
99
100 m = PyImport_AddModule("__main__");
101 globals = PyModule_GetDict(m);
102
103 str = PyString_FromString("pevent");
104 if (!str)
105 return -ENOMEM;
106
107 py_pevent = PyLong_FromUnsignedLong((unsigned long)pevent);
108 if (!py_pevent)
109 return -ENOMEM;
110
111 if (PyDict_SetItem(globals, str, py_pevent))
112 fprintf(stderr, "failed to insert pevent\n");
113
114 Py_DECREF(py_pevent);
115 Py_DECREF(str);
116
117 path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
118 if (!path)
119 return -1;
120
121 strcpy(path, home);
122 strcat(path, "/");
123 strcat(path, LOCAL_PLUGIN_DIR);
124
125 ret = load_plugins(globals, path);
126
127 free(path);
128
129 return ret;
130}
131
132int PEVENT_PLUGIN_UNLOADER(void)
133{
134 Py_Finalize();
135 return 0;
136}
diff --git a/trace-ftrace.c b/trace-ftrace.c
index af9ac8d..7cd5032 100644
--- a/trace-ftrace.c
+++ b/trace-ftrace.c
@@ -255,8 +255,13 @@ fgraph_ent_handler(struct trace_seq *s, struct record *record,
255 return trace_seq_putc(s, '!'); 255 return trace_seq_putc(s, '!');
256 256
257 rec = tracecmd_peek_data(tracecmd_curr_thread_handle, cpu); 257 rec = tracecmd_peek_data(tracecmd_curr_thread_handle, cpu);
258 if (rec) 258 if (rec) {
259 rec = get_return_for_leaf(s, cpu, pid, val, rec); 259 struct record *tmp;
260
261 tmp = get_return_for_leaf(s, cpu, pid, val, rec);
262 free_record(rec);
263 rec = tmp;
264 }
260 265
261 if (rec) { 266 if (rec) {
262 /* 267 /*
diff --git a/trace-input.c b/trace-input.c
index c39004a..9db96c7 100644
--- a/trace-input.c
+++ b/trace-input.c
@@ -660,8 +660,13 @@ void free_record(struct record *record)
660 if (!record) 660 if (!record)
661 return; 661 return;
662 662
663 if (record->locked) 663 if (!record->ref_count)
664 die("freeing record when it is locked!"); 664 die("record ref count is zero!");
665
666 record->ref_count--;
667 if (record->ref_count)
668 return;
669
665 record->data = NULL; 670 record->data = NULL;
666 671
667 __free_record(record); 672 __free_record(record);
@@ -675,7 +680,8 @@ static void free_next(struct tracecmd_input *handle, int cpu)
675 return; 680 return;
676 681
677 handle->cpu_data[cpu].next = NULL; 682 handle->cpu_data[cpu].next = NULL;
678 __free_record(record); 683
684 free_record(record);
679} 685}
680 686
681/* 687/*
@@ -870,6 +876,7 @@ read_old_format(struct tracecmd_input *handle, void **ptr, int cpu)
870 return NULL; 876 return NULL;
871 memset(data, 0, sizeof(*data)); 877 memset(data, 0, sizeof(*data));
872 878
879 data->ref_count = 1;
873 data->ts = handle->cpu_data[cpu].timestamp; 880 data->ts = handle->cpu_data[cpu].timestamp;
874 data->size = length; 881 data->size = length;
875 data->data = *ptr; 882 data->data = *ptr;
@@ -902,11 +909,12 @@ peek_event(struct tracecmd_input *handle, unsigned long long offset,
902 free_next(handle, cpu); 909 free_next(handle, cpu);
903 910
904 do { 911 do {
905 free_record(record);
906 record = tracecmd_peek_data(handle, cpu); 912 record = tracecmd_peek_data(handle, cpu);
907 if (record && (record->offset + record->record_size) > offset) 913 if (record && (record->offset + record->record_size) > offset)
908 break; 914 break;
909 record = tracecmd_read_data(handle, cpu); 915 free_record(record);
916
917 free_next(handle, cpu);
910 } while (record); 918 } while (record);
911 919
912 return record; 920 return record;
@@ -919,8 +927,10 @@ read_event(struct tracecmd_input *handle, unsigned long long offset,
919 struct record *record; 927 struct record *record;
920 928
921 record = peek_event(handle, offset, cpu); 929 record = peek_event(handle, offset, cpu);
922 if (record) 930 if (record) {
931 free_record(record);
923 record = tracecmd_read_data(handle, cpu); 932 record = tracecmd_read_data(handle, cpu);
933 }
924 return record; 934 return record;
925} 935}
926 936
@@ -1301,7 +1311,7 @@ int tracecmd_set_cursor(struct tracecmd_input *handle,
1301 if (get_page(handle, cpu, page_offset) < 0) 1311 if (get_page(handle, cpu, page_offset) < 0)
1302 return -1; 1312 return -1;
1303 1313
1304 peek_event(handle, offset, cpu); 1314 free_record(peek_event(handle, offset, cpu));
1305 1315
1306 return 0; 1316 return 0;
1307} 1317}
@@ -1429,6 +1439,7 @@ tracecmd_translate_data(struct tracecmd_input *handle,
1429 return NULL; 1439 return NULL;
1430 memset(record, 0, sizeof(*record)); 1440 memset(record, 0, sizeof(*record));
1431 1441
1442 record->ref_count = 1;
1432 record->data = ptr; 1443 record->data = ptr;
1433 type_len = translate_data(handle, &record->data, &record->ts, &record->size); 1444 type_len = translate_data(handle, &record->data, &record->ts, &record->size);
1434 switch (type_len) { 1445 switch (type_len) {
@@ -1451,8 +1462,6 @@ tracecmd_translate_data(struct tracecmd_input *handle,
1451 * 1462 *
1452 * This returns the record at the current location of the CPU 1463 * This returns the record at the current location of the CPU
1453 * iterator. It does not increment the CPU iterator. 1464 * iterator. It does not increment the CPU iterator.
1454 *
1455 * NOTE: Do not free the record returned, it is stored in the @handle.
1456 */ 1465 */
1457struct record * 1466struct record *
1458tracecmd_peek_data(struct tracecmd_input *handle, int cpu) 1467tracecmd_peek_data(struct tracecmd_input *handle, int cpu)
@@ -1479,8 +1488,10 @@ tracecmd_peek_data(struct tracecmd_input *handle, int cpu)
1479 if (!record->data) 1488 if (!record->data)
1480 die("Something freed the record"); 1489 die("Something freed the record");
1481 1490
1482 if (handle->cpu_data[cpu].timestamp == record->ts) 1491 if (handle->cpu_data[cpu].timestamp == record->ts) {
1492 record->ref_count++;
1483 return record; 1493 return record;
1494 }
1484 1495
1485 /* 1496 /*
1486 * The timestamp changed, which means the cached 1497 * The timestamp changed, which means the cached
@@ -1553,6 +1564,7 @@ read_again:
1553 record->data = ptr; 1564 record->data = ptr;
1554 record->offset = handle->cpu_data[cpu].offset + index; 1565 record->offset = handle->cpu_data[cpu].offset + index;
1555 record->missed_events = missed_events; 1566 record->missed_events = missed_events;
1567 record->ref_count = 2; /* will be returned and stored in page */
1556 1568
1557 ptr += length; 1569 ptr += length;
1558 1570
@@ -1561,7 +1573,6 @@ read_again:
1561 1573
1562 record->record_size = handle->cpu_data[cpu].index - index; 1574 record->record_size = handle->cpu_data[cpu].index - index;
1563 record->private = page; 1575 record->private = page;
1564 record->locked = 1;
1565 page->ref_count++; 1576 page->ref_count++;
1566 1577
1567 return record; 1578 return record;
@@ -1585,7 +1596,7 @@ tracecmd_read_data(struct tracecmd_input *handle, int cpu)
1585 record = tracecmd_peek_data(handle, cpu); 1596 record = tracecmd_peek_data(handle, cpu);
1586 handle->cpu_data[cpu].next = NULL; 1597 handle->cpu_data[cpu].next = NULL;
1587 if (record) 1598 if (record)
1588 record->locked = 0; 1599 record->ref_count--;
1589 1600
1590 return record; 1601 return record;
1591} 1602}
@@ -1627,6 +1638,7 @@ tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu)
1627 ts = record->ts; 1638 ts = record->ts;
1628 next = cpu; 1639 next = cpu;
1629 } 1640 }
1641 free_record(record);
1630 } 1642 }
1631 1643
1632 if (next >= 0) { 1644 if (next >= 0) {
@@ -1647,13 +1659,12 @@ tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu)
1647 * @record is the first record, NULL is returned. The cursor is set 1659 * @record is the first record, NULL is returned. The cursor is set
1648 * as if the previous record was read by tracecmd_read_data(). 1660 * as if the previous record was read by tracecmd_read_data().
1649 * 1661 *
1650 * @record can not be NULL, otherwise NULL is returned. 1662 * @record can not be NULL, otherwise NULL is returned; the
1663 * record ownership goes to this function.
1651 * 1664 *
1652 * Note, this is not that fast of an algorithm, since it needs 1665 * Note, this is not that fast of an algorithm, since it needs
1653 * to build the timestamp for the record. 1666 * to build the timestamp for the record.
1654 * 1667 *
1655 * Note 2: This may free any record allocoted with tracecmd_peek_data().
1656 *
1657 * The record returned must be freed with free_record(). 1668 * The record returned must be freed with free_record().
1658 */ 1669 */
1659struct record * 1670struct record *
@@ -1674,7 +1685,7 @@ tracecmd_read_prev(struct tracecmd_input *handle, struct record *record)
1674 page_offset = calc_page_offset(handle, offset); 1685 page_offset = calc_page_offset(handle, offset);
1675 index = offset - page_offset; 1686 index = offset - page_offset;
1676 1687
1677 /* Note, the record passed in could have been a peek */ 1688 free_record(record);
1678 free_next(handle, cpu); 1689 free_next(handle, cpu);
1679 1690
1680 /* Reset the cursor */ 1691 /* Reset the cursor */
diff --git a/trace-plot-cpu.c b/trace-plot-cpu.c
index 50d720b..fdfbdf7 100644
--- a/trace-plot-cpu.c
+++ b/trace-plot-cpu.c
@@ -137,7 +137,7 @@ static int cpu_plot_display_last_event(struct graph_info *ginfo,
137 record = tracecmd_peek_data(ginfo->handle, cpu); 137 record = tracecmd_peek_data(ginfo->handle, cpu);
138 if (record) 138 if (record)
139 offset = record->offset; 139 offset = record->offset;
140 /* Don't need to free a peek */ 140 free_record(record);
141 141
142 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); 142 tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time);
143 143
diff --git a/trace-plot-task.c b/trace-plot-task.c
index 0c9f8e0..71fa731 100644
--- a/trace-plot-task.c
+++ b/trace-plot-task.c
@@ -183,6 +183,7 @@ static struct offset_cache *save_offsets(struct graph_info *ginfo)
183 record = tracecmd_peek_data(ginfo->handle, cpu); 183 record = tracecmd_peek_data(ginfo->handle, cpu);
184 if (record) 184 if (record)
185 offsets->offsets[cpu] = record->offset; 185 offsets->offsets[cpu] = record->offset;
186 free_record(record);
186 } 187 }
187 188
188 return offsets; 189 return offsets;
diff --git a/trace-util.c b/trace-util.c
index 7c0481a..4fd1d38 100644
--- a/trace-util.c
+++ b/trace-util.c
@@ -212,7 +212,7 @@ load_plugin(struct pevent *pevent, struct plugin_list *plugin_list,
212 strcat(plugin, "/"); 212 strcat(plugin, "/");
213 strcat(plugin, file); 213 strcat(plugin, file);
214 214
215 handle = dlopen(plugin, RTLD_NOW); 215 handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
216 if (!handle) { 216 if (!handle) {
217 warning("cound not load plugin '%s'\n%s\n", 217 warning("cound not load plugin '%s'\n%s\n",
218 plugin, dlerror()); 218 plugin, dlerror());
diff --git a/tracecmd.py b/tracecmd.py
index 6b05e23..ad80ccd 100644
--- a/tracecmd.py
+++ b/tracecmd.py
@@ -18,6 +18,7 @@
18# 2009-Dec-17: Initial version by Darren Hart <dvhltc@us.ibm.com> 18# 2009-Dec-17: Initial version by Darren Hart <dvhltc@us.ibm.com>
19# 19#
20 20
21from functools import update_wrapper
21from ctracecmd import * 22from ctracecmd import *
22 23
23""" 24"""
@@ -31,13 +32,33 @@ and it is recommended applications not use it directly.
31TODO: consider a complete class hierarchy of ftrace events... 32TODO: consider a complete class hierarchy of ftrace events...
32""" 33"""
33 34
35def cached_property(func, name=None):
36 if name is None:
37 name = func.__name__
38 def _get(self):
39 try:
40 return self.__cached_properties[name]
41 except AttributeError:
42 self.__cached_properties = {}
43 except KeyError:
44 pass
45 value = func(self)
46 self.__cached_properties[name] = value
47 return value
48 update_wrapper(_get, func)
49 def _del(self):
50 self.__cached_properties.pop(name, None)
51 return property(_get, None, _del)
52
34class Event(object): 53class Event(object):
35 def __init__(self, trace, record, cpu): 54 """
36 self.trace = trace 55 This class can be used to access event data
37 self.rec = record 56 according to an event's record and format.
38 self.cpu = cpu 57 """
39 type = pevent_data_type(trace.pe, record) 58 def __init__(self, pevent, record, format):
40 self.format = pevent_data_event_from_type(trace.pe, type) 59 self._pevent = pevent
60 self._record = record
61 self._format = format
41 62
42 def __str__(self): 63 def __str__(self):
43 return "%d.%d CPU%d %s: pid=%d comm=%s type=%d" % \ 64 return "%d.%d CPU%d %s: pid=%d comm=%s type=%d" % \
@@ -45,35 +66,105 @@ class Event(object):
45 self.num_field("common_pid"), self.comm, self.type) 66 self.num_field("common_pid"), self.comm, self.type)
46 67
47 def __del__(self): 68 def __del__(self):
48 free_record(self.rec); 69 free_record(self._record)
70
71 def __getitem__(self, n):
72 f = pevent_find_field(self._format, n)
73 if f is None:
74 raise KeyError("no field '%s'" % n)
75 return Field(self._record, f)
49 76
77 def keys(self):
78 return py_format_get_keys(self._format)
50 79
51 # TODO: consider caching the results of the properties 80 @cached_property
52 @property
53 def comm(self): 81 def comm(self):
54 return self.trace.comm_from_pid(self.pid) 82 return pevent_data_comm_from_pid(self._pevent, self.pid)
55 83
56 @property 84 @cached_property
85 def cpu(self):
86 return record_cpu_get(self._record)
87
88 @cached_property
57 def name(self): 89 def name(self):
58 return event_format_name_get(self.format) 90 return event_format_name_get(self._format)
59 91
60 @property 92 @cached_property
61 def pid(self): 93 def pid(self):
62 return pevent_data_pid(self.trace.pe, self.rec) 94 return pevent_data_pid(self._pevent, self._record)
63 95
64 @property 96 @cached_property
65 def ts(self): 97 def ts(self):
66 return record_ts_get(self.rec) 98 return record_ts_get(self._record)
67 99
68 @property 100 @cached_property
69 def type(self): 101 def type(self):
70 return pevent_data_type(self.trace.pe, self.rec) 102 return pevent_data_type(self._pevent, self._record)
71 103
72 def num_field(self, name): 104 def num_field(self, name):
73 f = pevent_find_any_field(self.format, name) 105 f = pevent_find_any_field(self._format, name)
74 val = pevent_read_number_field_py(f, record_data_get(self.rec)) 106 if f is None:
107 return None
108 ret, val = pevent_read_number_field(f, record_data_get(self._record))
109 if ret:
110 return None
75 return val 111 return val
76 112
113 def str_field(self, name):
114 f = pevent_find_any_field(self._format, name)
115 if f is None:
116 return None
117 return py_field_get_str(f, self._record)
118
119class TraceSeq(object):
120 def __init__(self, trace_seq):
121 self._trace_seq = trace_seq
122
123 def puts(self, s):
124 return trace_seq_puts(self._trace_seq, s)
125
126class FieldError(Exception):
127 pass
128
129class Field(object):
130 def __init__(self, record, field):
131 self._record = record
132 self._field = field
133
134 @cached_property
135 def data(self):
136 return py_field_get_data(self._field, self._record)
137
138 def __long__(self):
139 ret, val = pevent_read_number_field(self._field,
140 record_data_get(self._record))
141 if ret:
142 raise FieldError("Not a number field")
143 return val
144 __int__ = __long__
145
146 def __str__(self):
147 return py_field_get_str(self._field, self._record)
148
149class PEvent(object):
150 def __init__(self, pevent):
151 self._pevent = pevent
152
153 def _handler(self, cb, s, record, event_fmt):
154 return cb(TraceSeq(s), Event(self._pevent, record, event_fmt))
155
156 def register_event_handler(self, subsys, event_name, callback):
157 l = lambda s, r, e: self._handler(callback, s, r, e)
158
159 py_pevent_register_event_handler(
160 self._pevent, -1, subsys, event_name, l)
161
162 @cached_property
163 def file_endian(self):
164 if pevent_is_file_bigendian(self._pevent):
165 return '>'
166 return '<'
167
77 168
78class FileFormatError(Exception): 169class FileFormatError(Exception):
79 pass 170 pass
@@ -86,45 +177,43 @@ class Trace(object):
86 used to manage the trace and extract events from it. 177 used to manage the trace and extract events from it.
87 """ 178 """
88 def __init__(self, filename): 179 def __init__(self, filename):
89 self.handle = tracecmd_open(filename) 180 self._handle = tracecmd_alloc(filename)
90 181
91 if tracecmd_read_headers(self.handle): 182 if tracecmd_read_headers(self._handle):
92 raise FileFormatError("Invalid headers") 183 raise FileFormatError("Invalid headers")
93 184
94 if tracecmd_init_data(self.handle): 185 if tracecmd_init_data(self._handle):
95 raise FileFormatError("Failed to init data") 186 raise FileFormatError("Failed to init data")
96 187
97 self.pe = tracecmd_get_pevent(self.handle) 188 self._pevent = tracecmd_get_pevent(self._handle)
98 189
99 @property 190 @cached_property
100 def cpus(self): 191 def cpus(self):
101 return tracecmd_cpus(self.handle) 192 return tracecmd_cpus(self._handle)
102 193
103 def read_event(self, cpu): 194 def read_event(self, cpu):
104 rec = tracecmd_read_data(self.handle, cpu) 195 rec = tracecmd_read_data(self._handle, cpu)
105 if rec: 196 if rec:
106 #rec.acquire() 197 type = pevent_data_type(self._pevent, rec)
107 #rec.thisown = 1 198 format = pevent_data_event_from_type(self._pevent, type)
108 return Event(self, rec, cpu) 199 # rec ownership goes over to Event instance
200 return Event(self._pevent, rec, format)
109 return None 201 return None
110 202
111 def read_event_at(self, offset): 203 def read_event_at(self, offset):
112 res = tracecmd_read_at(self.handle, offset) 204 res = tracecmd_read_at(self._handle, offset)
113 # SWIG only returns the CPU if the record is None for some reason 205 # SWIG only returns the CPU if the record is None for some reason
114 if isinstance(res, int): 206 if isinstance(res, int):
115 return None 207 return None
116 rec,cpu = res 208 rec, cpu = res
117 #rec.acquire() 209 type = pevent_data_type(self._pevent, rec)
118 #rec.thisown = 1 210 format = pevent_data_event_from_type(self._pevent, type)
119 ev = Event(self, rec, cpu) 211 # rec ownership goes over to Event instance
120 return ev 212 return Event(self._pevent, rec, format)
121 213
122 def peek_event(self, cpu): 214 def peek_event(self, cpu):
123 pass 215 pass
124 216
125 def comm_from_pid(self, pid):
126 return pevent_data_comm_from_pid(self.pe, pid)
127
128 217
129# Basic builtin test, execute module directly 218# Basic builtin test, execute module directly
130if __name__ == "__main__": 219if __name__ == "__main__":