aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2010-05-20 05:42:51 -0400
committerJohannes Berg <johannes@sipsolutions.net>2010-05-25 07:17:03 -0400
commit339321b326d241dcf6490ed910e8122486e29cd6 (patch)
treed4cc06dbc9ad286c1df9105ca253f449e91f262a
parentdf4d5ba4cb86f1055160559c9b7024e3dbf76a38 (diff)
python plugin
This adds a python plugin that can in turn load plugins written in python. To make it work, trace-cmd needs to load plugin modules with RTLD_GLOBAL so that the python interpreter's symbols will be available to python C extension modules. Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
-rw-r--r--Documentation/README.PythonPlugin127
-rw-r--r--Makefile19
-rw-r--r--ctracecmd.i6
-rw-r--r--plugin_python.c136
-rw-r--r--trace-util.c2
5 files changed, 289 insertions, 1 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 726d28e..a4a89ed 100644
--- a/Makefile
+++ b/Makefile
@@ -439,6 +439,8 @@ 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) ctracecmd.i 446ctracecmd.so: $(TCMD_LIB_OBJS) ctracecmd.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 624fb63..a0baa3c 100644
--- a/ctracecmd.i
+++ b/ctracecmd.i
@@ -34,6 +34,12 @@ static int python_callback(struct trace_seq *s,
34 struct event_format *event, 34 struct event_format *event,
35 void *context); 35 void *context);
36 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
37void py_pevent_register_event_handler(struct pevent *pevent, int id, 43void py_pevent_register_event_handler(struct pevent *pevent, int id,
38 char *subsys, char *evname, 44 char *subsys, char *evname,
39 PyObject *pyfunc) 45 PyObject *pyfunc)
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-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());