Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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 <ctype.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <dlfcn.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #include "event-parse.h"
31 #include "event-utils.h"
32
33 #define LOCAL_PLUGIN_DIR ".traceevent/plugins"
34
35 static struct registered_plugin_options {
36         struct registered_plugin_options        *next;
37         struct pevent_plugin_option             *options;
38 } *registered_options;
39
40 static struct trace_plugin_options {
41         struct trace_plugin_options     *next;
42         char                            *plugin;
43         char                            *option;
44         char                            *value;
45 } *trace_plugin_options;
46
47 struct plugin_list {
48         struct plugin_list      *next;
49         char                    *name;
50         void                    *handle;
51 };
52
53 static void lower_case(char *str)
54 {
55         if (!str)
56                 return;
57         for (; *str; str++)
58                 *str = tolower(*str);
59 }
60
61 static int update_option_value(struct pevent_plugin_option *op, const char *val)
62 {
63         char *op_val;
64
65         if (!val) {
66                 /* toggle, only if option is boolean */
67                 if (op->value)
68                         /* Warn? */
69                         return 0;
70                 op->set ^= 1;
71                 return 0;
72         }
73
74         /*
75          * If the option has a value then it takes a string
76          * otherwise the option is a boolean.
77          */
78         if (op->value) {
79                 op->value = val;
80                 return 0;
81         }
82
83         /* Option is boolean, must be either "1", "0", "true" or "false" */
84
85         op_val = strdup(val);
86         if (!op_val)
87                 return -1;
88         lower_case(op_val);
89
90         if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
91                 op->set = 1;
92         else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0)
93                 op->set = 0;
94         free(op_val);
95
96         return 0;
97 }
98
99 /**
100  * traceevent_plugin_list_options - get list of plugin options
101  *
102  * Returns an array of char strings that list the currently registered
103  * plugin options in the format of <plugin>:<option>. This list can be
104  * used by toggling the option.
105  *
106  * Returns NULL if there's no options registered. On error it returns
107  * INVALID_PLUGIN_LIST_OPTION
108  *
109  * Must be freed with traceevent_plugin_free_options_list().
110  */
111 char **traceevent_plugin_list_options(void)
112 {
113         struct registered_plugin_options *reg;
114         struct pevent_plugin_option *op;
115         char **list = NULL;
116         char *name;
117         int count = 0;
118
119         for (reg = registered_options; reg; reg = reg->next) {
120                 for (op = reg->options; op->name; op++) {
121                         char *alias = op->plugin_alias ? op->plugin_alias : op->file;
122                         char **temp = list;
123
124                         name = malloc(strlen(op->name) + strlen(alias) + 2);
125                         if (!name)
126                                 goto err;
127
128                         sprintf(name, "%s:%s", alias, op->name);
129                         list = realloc(list, count + 2);
130                         if (!list) {
131                                 list = temp;
132                                 free(name);
133                                 goto err;
134                         }
135                         list[count++] = name;
136                         list[count] = NULL;
137                 }
138         }
139         return list;
140
141  err:
142         while (--count >= 0)
143                 free(list[count]);
144         free(list);
145
146         return INVALID_PLUGIN_LIST_OPTION;
147 }
148
149 void traceevent_plugin_free_options_list(char **list)
150 {
151         int i;
152
153         if (!list)
154                 return;
155
156         if (list == INVALID_PLUGIN_LIST_OPTION)
157                 return;
158
159         for (i = 0; list[i]; i++)
160                 free(list[i]);
161
162         free(list);
163 }
164
165 static int
166 update_option(const char *file, struct pevent_plugin_option *option)
167 {
168         struct trace_plugin_options *op;
169         char *plugin;
170         int ret = 0;
171
172         if (option->plugin_alias) {
173                 plugin = strdup(option->plugin_alias);
174                 if (!plugin)
175                         return -1;
176         } else {
177                 char *p;
178                 plugin = strdup(file);
179                 if (!plugin)
180                         return -1;
181                 p = strstr(plugin, ".");
182                 if (p)
183                         *p = '\0';
184         }
185
186         /* first look for named options */
187         for (op = trace_plugin_options; op; op = op->next) {
188                 if (!op->plugin)
189                         continue;
190                 if (strcmp(op->plugin, plugin) != 0)
191                         continue;
192                 if (strcmp(op->option, option->name) != 0)
193                         continue;
194
195                 ret = update_option_value(option, op->value);
196                 if (ret)
197                         goto out;
198                 break;
199         }
200
201         /* first look for unnamed options */
202         for (op = trace_plugin_options; op; op = op->next) {
203                 if (op->plugin)
204                         continue;
205                 if (strcmp(op->option, option->name) != 0)
206                         continue;
207
208                 ret = update_option_value(option, op->value);
209                 break;
210         }
211
212  out:
213         free(plugin);
214         return ret;
215 }
216
217 /**
218  * traceevent_plugin_add_options - Add a set of options by a plugin
219  * @name: The name of the plugin adding the options
220  * @options: The set of options being loaded
221  *
222  * Sets the options with the values that have been added by user.
223  */
224 int traceevent_plugin_add_options(const char *name,
225                                   struct pevent_plugin_option *options)
226 {
227         struct registered_plugin_options *reg;
228
229         reg = malloc(sizeof(*reg));
230         if (!reg)
231                 return -1;
232         reg->next = registered_options;
233         reg->options = options;
234         registered_options = reg;
235
236         while (options->name) {
237                 update_option(name, options);
238                 options++;
239         }
240         return 0;
241 }
242
243 /**
244  * traceevent_plugin_remove_options - remove plugin options that were registered
245  * @options: Options to removed that were registered with traceevent_plugin_add_options
246  */
247 void traceevent_plugin_remove_options(struct pevent_plugin_option *options)
248 {
249         struct registered_plugin_options **last;
250         struct registered_plugin_options *reg;
251
252         for (last = &registered_options; *last; last = &(*last)->next) {
253                 if ((*last)->options == options) {
254                         reg = *last;
255                         *last = reg->next;
256                         free(reg);
257                         return;
258                 }
259         }
260 }
261
262 /**
263  * traceevent_print_plugins - print out the list of plugins loaded
264  * @s: the trace_seq descripter to write to
265  * @prefix: The prefix string to add before listing the option name
266  * @suffix: The suffix string ot append after the option name
267  * @list: The list of plugins (usually returned by traceevent_load_plugins()
268  *
269  * Writes to the trace_seq @s the list of plugins (files) that is
270  * returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
271  * @prefix = "  ", @suffix = "\n".
272  */
273 void traceevent_print_plugins(struct trace_seq *s,
274                               const char *prefix, const char *suffix,
275                               const struct plugin_list *list)
276 {
277         while (list) {
278                 trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
279                 list = list->next;
280         }
281 }
282
283 static void
284 load_plugin(struct pevent *pevent, const char *path,
285             const char *file, void *data)
286 {
287         struct plugin_list **plugin_list = data;
288         pevent_plugin_load_func func;
289         struct plugin_list *list;
290         const char *alias;
291         char *plugin;
292         void *handle;
293
294         plugin = malloc(strlen(path) + strlen(file) + 2);
295         if (!plugin) {
296                 warning("could not allocate plugin memory\n");
297                 return;
298         }
299
300         strcpy(plugin, path);
301         strcat(plugin, "/");
302         strcat(plugin, file);
303
304         handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
305         if (!handle) {
306                 warning("could not load plugin '%s'\n%s\n",
307                         plugin, dlerror());
308                 goto out_free;
309         }
310
311         alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME);
312         if (!alias)
313                 alias = file;
314
315         func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
316         if (!func) {
317                 warning("could not find func '%s' in plugin '%s'\n%s\n",
318                         PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
319                 goto out_free;
320         }
321
322         list = malloc(sizeof(*list));
323         if (!list) {
324                 warning("could not allocate plugin memory\n");
325                 goto out_free;
326         }
327
328         list->next = *plugin_list;
329         list->handle = handle;
330         list->name = plugin;
331         *plugin_list = list;
332
333         pr_stat("registering plugin: %s", plugin);
334         func(pevent);
335         return;
336
337  out_free:
338         free(plugin);
339 }
340
341 static void
342 load_plugins_dir(struct pevent *pevent, const char *suffix,
343                  const char *path,
344                  void (*load_plugin)(struct pevent *pevent,
345                                      const char *path,
346                                      const char *name,
347                                      void *data),
348                  void *data)
349 {
350         struct dirent *dent;
351         struct stat st;
352         DIR *dir;
353         int ret;
354
355         ret = stat(path, &st);
356         if (ret < 0)
357                 return;
358
359         if (!S_ISDIR(st.st_mode))
360                 return;
361
362         dir = opendir(path);
363         if (!dir)
364                 return;
365
366         while ((dent = readdir(dir))) {
367                 const char *name = dent->d_name;
368
369                 if (strcmp(name, ".") == 0 ||
370                     strcmp(name, "..") == 0)
371                         continue;
372
373                 /* Only load plugins that end in suffix */
374                 if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
375                         continue;
376
377                 load_plugin(pevent, path, name, data);
378         }
379
380         closedir(dir);
381 }
382
383 static void
384 load_plugins(struct pevent *pevent, const char *suffix,
385              void (*load_plugin)(struct pevent *pevent,
386                                  const char *path,
387                                  const char *name,
388                                  void *data),
389              void *data)
390 {
391         char *home;
392         char *path;
393         char *envdir;
394
395         if (pevent->flags & PEVENT_DISABLE_PLUGINS)
396                 return;
397
398         /*
399          * If a system plugin directory was defined,
400          * check that first.
401          */
402 #ifdef PLUGIN_DIR
403         if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
404                 load_plugins_dir(pevent, suffix, PLUGIN_DIR,
405                                  load_plugin, data);
406 #endif
407
408         /*
409          * Next let the environment-set plugin directory
410          * override the system defaults.
411          */
412         envdir = getenv("TRACEEVENT_PLUGIN_DIR");
413         if (envdir)
414                 load_plugins_dir(pevent, suffix, envdir, load_plugin, data);
415
416         /*
417          * Now let the home directory override the environment
418          * or system defaults.
419          */
420         home = getenv("HOME");
421         if (!home)
422                 return;
423
424         path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
425         if (!path) {
426                 warning("could not allocate plugin memory\n");
427                 return;
428         }
429
430         strcpy(path, home);
431         strcat(path, "/");
432         strcat(path, LOCAL_PLUGIN_DIR);
433
434         load_plugins_dir(pevent, suffix, path, load_plugin, data);
435
436         free(path);
437 }
438
439 struct plugin_list*
440 traceevent_load_plugins(struct pevent *pevent)
441 {
442         struct plugin_list *list = NULL;
443
444         load_plugins(pevent, ".so", load_plugin, &list);
445         return list;
446 }
447
448 void
449 traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent)
450 {
451         pevent_plugin_unload_func func;
452         struct plugin_list *list;
453
454         while (plugin_list) {
455                 list = plugin_list;
456                 plugin_list = list->next;
457                 func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
458                 if (func)
459                         func(pevent);
460                 dlclose(list->handle);
461                 free(list->name);
462                 free(list);
463         }
464 }