Merge branch 'x86-cpufeature-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / tools / lib / traceevent / event-plugin.c
1 /*
2  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
3  *
4  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation;
8  * version 2.1 of the License (not later!)
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not,  see <http://www.gnu.org/licenses>
17  *
18  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <dlfcn.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include "event-parse.h"
30 #include "event-utils.h"
31
32 #define LOCAL_PLUGIN_DIR ".traceevent/plugins"
33
34 static struct registered_plugin_options {
35         struct registered_plugin_options        *next;
36         struct pevent_plugin_option             *options;
37 } *registered_options;
38
39 static struct trace_plugin_options {
40         struct trace_plugin_options     *next;
41         char                            *plugin;
42         char                            *option;
43         char                            *value;
44 } *trace_plugin_options;
45
46 struct plugin_list {
47         struct plugin_list      *next;
48         char                    *name;
49         void                    *handle;
50 };
51
52 /**
53  * traceevent_plugin_list_options - get list of plugin options
54  *
55  * Returns an array of char strings that list the currently registered
56  * plugin options in the format of <plugin>:<option>. This list can be
57  * used by toggling the option.
58  *
59  * Returns NULL if there's no options registered. On error it returns
60  * INVALID_PLUGIN_LIST_OPTION
61  *
62  * Must be freed with traceevent_plugin_free_options_list().
63  */
64 char **traceevent_plugin_list_options(void)
65 {
66         struct registered_plugin_options *reg;
67         struct pevent_plugin_option *op;
68         char **list = NULL;
69         char *name;
70         int count = 0;
71
72         for (reg = registered_options; reg; reg = reg->next) {
73                 for (op = reg->options; op->name; op++) {
74                         char *alias = op->plugin_alias ? op->plugin_alias : op->file;
75                         char **temp = list;
76
77                         name = malloc(strlen(op->name) + strlen(alias) + 2);
78                         if (!name)
79                                 goto err;
80
81                         sprintf(name, "%s:%s", alias, op->name);
82                         list = realloc(list, count + 2);
83                         if (!list) {
84                                 list = temp;
85                                 free(name);
86                                 goto err;
87                         }
88                         list[count++] = name;
89                         list[count] = NULL;
90                 }
91         }
92         return list;
93
94  err:
95         while (--count >= 0)
96                 free(list[count]);
97         free(list);
98
99         return INVALID_PLUGIN_LIST_OPTION;
100 }
101
102 void traceevent_plugin_free_options_list(char **list)
103 {
104         int i;
105
106         if (!list)
107                 return;
108
109         if (list == INVALID_PLUGIN_LIST_OPTION)
110                 return;
111
112         for (i = 0; list[i]; i++)
113                 free(list[i]);
114
115         free(list);
116 }
117
118 static int
119 update_option(const char *file, struct pevent_plugin_option *option)
120 {
121         struct trace_plugin_options *op;
122         char *plugin;
123
124         if (option->plugin_alias) {
125                 plugin = strdup(option->plugin_alias);
126                 if (!plugin)
127                         return -1;
128         } else {
129                 char *p;
130                 plugin = strdup(file);
131                 if (!plugin)
132                         return -1;
133                 p = strstr(plugin, ".");
134                 if (p)
135                         *p = '\0';
136         }
137
138         /* first look for named options */
139         for (op = trace_plugin_options; op; op = op->next) {
140                 if (!op->plugin)
141                         continue;
142                 if (strcmp(op->plugin, plugin) != 0)
143                         continue;
144                 if (strcmp(op->option, option->name) != 0)
145                         continue;
146
147                 option->value = op->value;
148                 option->set ^= 1;
149                 goto out;
150         }
151
152         /* first look for unnamed options */
153         for (op = trace_plugin_options; op; op = op->next) {
154                 if (op->plugin)
155                         continue;
156                 if (strcmp(op->option, option->name) != 0)
157                         continue;
158
159                 option->value = op->value;
160                 option->set ^= 1;
161                 break;
162         }
163
164  out:
165         free(plugin);
166         return 0;
167 }
168
169 /**
170  * traceevent_plugin_add_options - Add a set of options by a plugin
171  * @name: The name of the plugin adding the options
172  * @options: The set of options being loaded
173  *
174  * Sets the options with the values that have been added by user.
175  */
176 int traceevent_plugin_add_options(const char *name,
177                                   struct pevent_plugin_option *options)
178 {
179         struct registered_plugin_options *reg;
180
181         reg = malloc(sizeof(*reg));
182         if (!reg)
183                 return -1;
184         reg->next = registered_options;
185         reg->options = options;
186         registered_options = reg;
187
188         while (options->name) {
189                 update_option(name, options);
190                 options++;
191         }
192         return 0;
193 }
194
195 /**
196  * traceevent_plugin_remove_options - remove plugin options that were registered
197  * @options: Options to removed that were registered with traceevent_plugin_add_options
198  */
199 void traceevent_plugin_remove_options(struct pevent_plugin_option *options)
200 {
201         struct registered_plugin_options **last;
202         struct registered_plugin_options *reg;
203
204         for (last = &registered_options; *last; last = &(*last)->next) {
205                 if ((*last)->options == options) {
206                         reg = *last;
207                         *last = reg->next;
208                         free(reg);
209                         return;
210                 }
211         }
212 }
213
214 /**
215  * traceevent_print_plugins - print out the list of plugins loaded
216  * @s: the trace_seq descripter to write to
217  * @prefix: The prefix string to add before listing the option name
218  * @suffix: The suffix string ot append after the option name
219  * @list: The list of plugins (usually returned by traceevent_load_plugins()
220  *
221  * Writes to the trace_seq @s the list of plugins (files) that is
222  * returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
223  * @prefix = "  ", @suffix = "\n".
224  */
225 void traceevent_print_plugins(struct trace_seq *s,
226                               const char *prefix, const char *suffix,
227                               const struct plugin_list *list)
228 {
229         while (list) {
230                 trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
231                 list = list->next;
232         }
233 }
234
235 static void
236 load_plugin(struct pevent *pevent, const char *path,
237             const char *file, void *data)
238 {
239         struct plugin_list **plugin_list = data;
240         pevent_plugin_load_func func;
241         struct plugin_list *list;
242         const char *alias;
243         char *plugin;
244         void *handle;
245
246         plugin = malloc(strlen(path) + strlen(file) + 2);
247         if (!plugin) {
248                 warning("could not allocate plugin memory\n");
249                 return;
250         }
251
252         strcpy(plugin, path);
253         strcat(plugin, "/");
254         strcat(plugin, file);
255
256         handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
257         if (!handle) {
258                 warning("could not load plugin '%s'\n%s\n",
259                         plugin, dlerror());
260                 goto out_free;
261         }
262
263         alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME);
264         if (!alias)
265                 alias = file;
266
267         func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
268         if (!func) {
269                 warning("could not find func '%s' in plugin '%s'\n%s\n",
270                         PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
271                 goto out_free;
272         }
273
274         list = malloc(sizeof(*list));
275         if (!list) {
276                 warning("could not allocate plugin memory\n");
277                 goto out_free;
278         }
279
280         list->next = *plugin_list;
281         list->handle = handle;
282         list->name = plugin;
283         *plugin_list = list;
284
285         pr_stat("registering plugin: %s", plugin);
286         func(pevent);
287         return;
288
289  out_free:
290         free(plugin);
291 }
292
293 static void
294 load_plugins_dir(struct pevent *pevent, const char *suffix,
295                  const char *path,
296                  void (*load_plugin)(struct pevent *pevent,
297                                      const char *path,
298                                      const char *name,
299                                      void *data),
300                  void *data)
301 {
302         struct dirent *dent;
303         struct stat st;
304         DIR *dir;
305         int ret;
306
307         ret = stat(path, &st);
308         if (ret < 0)
309                 return;
310
311         if (!S_ISDIR(st.st_mode))
312                 return;
313
314         dir = opendir(path);
315         if (!dir)
316                 return;
317
318         while ((dent = readdir(dir))) {
319                 const char *name = dent->d_name;
320
321                 if (strcmp(name, ".") == 0 ||
322                     strcmp(name, "..") == 0)
323                         continue;
324
325                 /* Only load plugins that end in suffix */
326                 if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
327                         continue;
328
329                 load_plugin(pevent, path, name, data);
330         }
331
332         closedir(dir);
333 }
334
335 static void
336 load_plugins(struct pevent *pevent, const char *suffix,
337              void (*load_plugin)(struct pevent *pevent,
338                                  const char *path,
339                                  const char *name,
340                                  void *data),
341              void *data)
342 {
343         char *home;
344         char *path;
345         char *envdir;
346
347         if (pevent->flags & PEVENT_DISABLE_PLUGINS)
348                 return;
349
350         /*
351          * If a system plugin directory was defined,
352          * check that first.
353          */
354 #ifdef PLUGIN_DIR
355         if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
356                 load_plugins_dir(pevent, suffix, PLUGIN_DIR,
357                                  load_plugin, data);
358 #endif
359
360         /*
361          * Next let the environment-set plugin directory
362          * override the system defaults.
363          */
364         envdir = getenv("TRACEEVENT_PLUGIN_DIR");
365         if (envdir)
366                 load_plugins_dir(pevent, suffix, envdir, load_plugin, data);
367
368         /*
369          * Now let the home directory override the environment
370          * or system defaults.
371          */
372         home = getenv("HOME");
373         if (!home)
374                 return;
375
376         path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
377         if (!path) {
378                 warning("could not allocate plugin memory\n");
379                 return;
380         }
381
382         strcpy(path, home);
383         strcat(path, "/");
384         strcat(path, LOCAL_PLUGIN_DIR);
385
386         load_plugins_dir(pevent, suffix, path, load_plugin, data);
387
388         free(path);
389 }
390
391 struct plugin_list*
392 traceevent_load_plugins(struct pevent *pevent)
393 {
394         struct plugin_list *list = NULL;
395
396         load_plugins(pevent, ".so", load_plugin, &list);
397         return list;
398 }
399
400 void
401 traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent)
402 {
403         pevent_plugin_unload_func func;
404         struct plugin_list *list;
405
406         while (plugin_list) {
407                 list = plugin_list;
408                 plugin_list = list->next;
409                 func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
410                 if (func)
411                         func(pevent);
412                 dlclose(list->handle);
413                 free(list->name);
414                 free(list);
415         }
416 }