tracing/events: fix compile for modules disabled
[safe/jmp/linux-2.6] / kernel / trace / trace_events.c
index 1d07f80..6591d83 100644 (file)
@@ -3,6 +3,9 @@
  *
  * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  *
+ *  - Added format output of fields of the trace point.
+ *    This was based off of work by Tom Zanussi <tzanussi@gmail.com>.
+ *
  */
 
 #include <linux/debugfs.h>
 #include <linux/module.h>
 #include <linux/ctype.h>
 
-#include "trace.h"
+#include "trace_output.h"
 
 #define TRACE_SYSTEM "TRACE_SYSTEM"
 
-#define events_for_each(event)                                         \
-       for (event = __start_ftrace_events;                             \
-            (unsigned long)event < (unsigned long)__stop_ftrace_events; \
-            event++)
+static DEFINE_MUTEX(event_mutex);
+
+LIST_HEAD(ftrace_events);
 
-void event_trace_printk(unsigned long ip, const char *fmt, ...)
+int trace_define_field(struct ftrace_event_call *call, char *type,
+                      char *name, int offset, int size)
 {
-       va_list ap;
+       struct ftrace_event_field *field;
+
+       field = kzalloc(sizeof(*field), GFP_KERNEL);
+       if (!field)
+               goto err;
+
+       field->name = kstrdup(name, GFP_KERNEL);
+       if (!field->name)
+               goto err;
+
+       field->type = kstrdup(type, GFP_KERNEL);
+       if (!field->type)
+               goto err;
 
-       va_start(ap, fmt);
-       tracing_record_cmdline(current);
-       trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap);
-       va_end(ap);
+       field->offset = offset;
+       field->size = size;
+       list_add(&field->link, &call->fields);
+
+       return 0;
+
+err:
+       if (field) {
+               kfree(field->name);
+               kfree(field->type);
+       }
+       kfree(field);
+
+       return -ENOMEM;
 }
+EXPORT_SYMBOL_GPL(trace_define_field);
 
 static void ftrace_clear_events(void)
 {
-       struct ftrace_event_call *call = (void *)__start_ftrace_events;
-
+       struct ftrace_event_call *call;
 
-       while ((unsigned long)call < (unsigned long)__stop_ftrace_events) {
+       list_for_each_entry(call, &ftrace_events, list) {
 
                if (call->enabled) {
                        call->enabled = 0;
                        call->unregfunc();
                }
-               call++;
        }
 }
 
@@ -54,29 +78,19 @@ static void ftrace_event_enable_disable(struct ftrace_event_call *call,
                        call->enabled = 0;
                        call->unregfunc();
                }
-               if (call->raw_enabled) {
-                       call->raw_enabled = 0;
-                       call->raw_unreg();
-               }
                break;
        case 1:
-               if (!call->enabled &&
-                   (call->type & TRACE_EVENT_TYPE_PRINTF)) {
+               if (!call->enabled) {
                        call->enabled = 1;
                        call->regfunc();
                }
-               if (!call->raw_enabled &&
-                   (call->type & TRACE_EVENT_TYPE_RAW)) {
-                       call->raw_enabled = 1;
-                       call->raw_reg();
-               }
                break;
        }
 }
 
 static int ftrace_set_clr_event(char *buf, int set)
 {
-       struct ftrace_event_call *call = __start_ftrace_events;
+       struct ftrace_event_call *call;
        char *event = NULL, *sub = NULL, *match;
        int ret = -EINVAL;
 
@@ -104,9 +118,10 @@ static int ftrace_set_clr_event(char *buf, int set)
                        event = NULL;
        }
 
-       events_for_each(call) {
+       mutex_lock(&event_mutex);
+       list_for_each_entry(call, &ftrace_events, list) {
 
-               if (!call->name)
+               if (!call->name || !call->regfunc)
                        continue;
 
                if (match &&
@@ -124,6 +139,8 @@ static int ftrace_set_clr_event(char *buf, int set)
 
                ret = 0;
        }
+       mutex_unlock(&event_mutex);
+
        return ret;
 }
 
@@ -143,6 +160,10 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
        if (!cnt || cnt < 0)
                return 0;
 
+       ret = tracing_update_buffers();
+       if (ret < 0)
+               return ret;
+
        ret = get_user(ch, ubuf++);
        if (ret)
                return ret;
@@ -204,15 +225,28 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
 static void *
 t_next(struct seq_file *m, void *v, loff_t *pos)
 {
-       struct ftrace_event_call *call = m->private;
-       struct ftrace_event_call *next = call;
+       struct list_head *list = m->private;
+       struct ftrace_event_call *call;
 
        (*pos)++;
 
-       if ((unsigned long)call >= (unsigned long)__stop_ftrace_events)
-               return NULL;
+       for (;;) {
+               if (list == &ftrace_events)
+                       return NULL;
+
+               call = list_entry(list, struct ftrace_event_call, list);
+
+               /*
+                * The ftrace subsystem is for showing formats only.
+                * They can not be enabled or disabled via the event files.
+                */
+               if (call->regfunc)
+                       break;
 
-       m->private = ++next;
+               list = list->next;
+       }
+
+       m->private = list->next;
 
        return call;
 }
@@ -225,22 +259,23 @@ static void *t_start(struct seq_file *m, loff_t *pos)
 static void *
 s_next(struct seq_file *m, void *v, loff_t *pos)
 {
-       struct ftrace_event_call *call = m->private;
-       struct ftrace_event_call *next;
+       struct list_head *list = m->private;
+       struct ftrace_event_call *call;
 
        (*pos)++;
 
  retry:
-       if ((unsigned long)call >= (unsigned long)__stop_ftrace_events)
+       if (list == &ftrace_events)
                return NULL;
 
+       call = list_entry(list, struct ftrace_event_call, list);
+
        if (!call->enabled) {
-               call++;
+               list = list->next;
                goto retry;
        }
 
-       next = call;
-       m->private = ++next;
+       m->private = list->next;
 
        return call;
 }
@@ -280,7 +315,7 @@ ftrace_event_seq_open(struct inode *inode, struct file *file)
        if (!ret) {
                struct seq_file *m = file->private_data;
 
-               m->private = __start_ftrace_events;
+               m->private = ftrace_events.next;
        }
        return ret;
 }
@@ -292,7 +327,7 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
        struct ftrace_event_call *call = filp->private_data;
        char *buf;
 
-       if (call->enabled || call->raw_enabled)
+       if (call->enabled)
                buf = "1\n";
        else
                buf = "0\n";
@@ -321,10 +356,16 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
        if (ret < 0)
                return ret;
 
+       ret = tracing_update_buffers();
+       if (ret < 0)
+               return ret;
+
        switch (val) {
        case 0:
        case 1:
+               mutex_lock(&event_mutex);
                ftrace_event_enable_disable(call, val);
+               mutex_unlock(&event_mutex);
                break;
 
        default:
@@ -336,105 +377,233 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
        return cnt;
 }
 
+#undef FIELD
+#define FIELD(type, name)                                              \
+       #type, "common_" #name, offsetof(typeof(field), name),          \
+               sizeof(field.name)
+
+static int trace_write_header(struct trace_seq *s)
+{
+       struct trace_entry field;
+
+       /* struct trace_entry */
+       return trace_seq_printf(s,
+                               "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
+                               "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
+                               "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
+                               "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
+                               "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
+                               "\n",
+                               FIELD(unsigned char, type),
+                               FIELD(unsigned char, flags),
+                               FIELD(unsigned char, preempt_count),
+                               FIELD(int, pid),
+                               FIELD(int, tgid));
+}
+
 static ssize_t
-event_type_read(struct file *filp, char __user *ubuf, size_t cnt,
-               loff_t *ppos)
+event_format_read(struct file *filp, char __user *ubuf, size_t cnt,
+                 loff_t *ppos)
 {
        struct ftrace_event_call *call = filp->private_data;
-       char buf[16];
-       int r = 0;
+       struct trace_seq *s;
+       char *buf;
+       int r;
 
-       if (call->type & TRACE_EVENT_TYPE_PRINTF)
-               r += sprintf(buf, "printf\n");
+       if (*ppos)
+               return 0;
+
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       trace_seq_init(s);
+
+       /* If any of the first writes fail, so will the show_format. */
+
+       trace_seq_printf(s, "name: %s\n", call->name);
+       trace_seq_printf(s, "ID: %d\n", call->id);
+       trace_seq_printf(s, "format:\n");
+       trace_write_header(s);
 
-       if (call->type & TRACE_EVENT_TYPE_RAW)
-               r += sprintf(buf+r, "raw\n");
+       r = call->show_format(s);
+       if (!r) {
+               /*
+                * ug!  The format output is bigger than a PAGE!!
+                */
+               buf = "FORMAT TOO BIG\n";
+               r = simple_read_from_buffer(ubuf, cnt, ppos,
+                                             buf, strlen(buf));
+               goto out;
+       }
 
-       return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+       r = simple_read_from_buffer(ubuf, cnt, ppos,
+                                   s->buffer, s->len);
+ out:
+       kfree(s);
+       return r;
 }
 
 static ssize_t
-event_type_write(struct file *filp, const char __user *ubuf, size_t cnt,
-                loff_t *ppos)
+event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
 {
        struct ftrace_event_call *call = filp->private_data;
-       char buf[64];
+       struct trace_seq *s;
+       int r;
 
-       /*
-        * If there's only one type, we can't change it.
-        * And currently we always have printf type, and we
-        * may or may not have raw type.
-        *
-        * This is a redundant check, the file should be read
-        * only if this is the case anyway.
-        */
+       if (*ppos)
+               return 0;
+
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       trace_seq_init(s);
+       trace_seq_printf(s, "%d\n", call->id);
+
+       r = simple_read_from_buffer(ubuf, cnt, ppos,
+                                   s->buffer, s->len);
+       kfree(s);
+       return r;
+}
+
+static ssize_t
+event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
+                 loff_t *ppos)
+{
+       struct ftrace_event_call *call = filp->private_data;
+       struct trace_seq *s;
+       int r;
 
-       if (!call->raw_init)
-               return -EPERM;
+       if (*ppos)
+               return 0;
+
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       trace_seq_init(s);
+
+       filter_print_preds(call->preds, call->n_preds, s);
+       r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
+
+       kfree(s);
+
+       return r;
+}
+
+static ssize_t
+event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
+                  loff_t *ppos)
+{
+       struct ftrace_event_call *call = filp->private_data;
+       char buf[64], *pbuf = buf;
+       struct filter_pred *pred;
+       int err;
 
        if (cnt >= sizeof(buf))
                return -EINVAL;
 
        if (copy_from_user(&buf, ubuf, cnt))
                return -EFAULT;
+       buf[cnt] = '\0';
 
-       buf[cnt] = 0;
+       pred = kzalloc(sizeof(*pred), GFP_KERNEL);
+       if (!pred)
+               return -ENOMEM;
 
-       if (!strncmp(buf, "printf", 6) &&
-           (!buf[6] || isspace(buf[6]))) {
+       err = filter_parse(&pbuf, pred);
+       if (err < 0) {
+               filter_free_pred(pred);
+               return err;
+       }
 
-               call->type = TRACE_EVENT_TYPE_PRINTF;
+       if (pred->clear) {
+               filter_disable_preds(call);
+               filter_free_pred(pred);
+               return cnt;
+       }
 
-               /*
-                * If raw enabled, the disable it and enable
-                * printf type.
-                */
-               if (call->raw_enabled) {
-                       call->raw_enabled = 0;
-                       call->raw_unreg();
+       err = filter_add_pred(call, pred);
+       if (err < 0) {
+               filter_free_pred(pred);
+               return err;
+       }
 
-                       call->enabled = 1;
-                       call->regfunc();
-               }
+       filter_free_pred(pred);
 
-       } else if (!strncmp(buf, "raw", 3) &&
-           (!buf[3] || isspace(buf[3]))) {
+       *ppos += cnt;
 
-               call->type = TRACE_EVENT_TYPE_RAW;
+       return cnt;
+}
 
-               /*
-                * If printf enabled, the disable it and enable
-                * raw type.
-                */
-               if (call->enabled) {
-                       call->enabled = 0;
-                       call->unregfunc();
+static ssize_t
+subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
+                     loff_t *ppos)
+{
+       struct event_subsystem *system = filp->private_data;
+       struct trace_seq *s;
+       int r;
 
-                       call->raw_enabled = 1;
-                       call->raw_reg();
-               }
-       } else
-               return -EINVAL;
+       if (*ppos)
+               return 0;
 
-       *ppos += cnt;
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
 
-       return cnt;
+       trace_seq_init(s);
+
+       filter_print_preds(system->preds, system->n_preds, s);
+       r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
+
+       kfree(s);
+
+       return r;
 }
 
 static ssize_t
-event_available_types_read(struct file *filp, char __user *ubuf, size_t cnt,
-                          loff_t *ppos)
+subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
+                      loff_t *ppos)
 {
-       struct ftrace_event_call *call = filp->private_data;
-       char buf[16];
-       int r = 0;
+       struct event_subsystem *system = filp->private_data;
+       char buf[64], *pbuf = buf;
+       struct filter_pred *pred;
+       int err;
+
+       if (cnt >= sizeof(buf))
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, cnt))
+               return -EFAULT;
+       buf[cnt] = '\0';
+
+       pred = kzalloc(sizeof(*pred), GFP_KERNEL);
+       if (!pred)
+               return -ENOMEM;
+
+       err = filter_parse(&pbuf, pred);
+       if (err < 0) {
+               filter_free_pred(pred);
+               return err;
+       }
+
+       if (pred->clear) {
+               filter_free_subsystem_preds(system);
+               filter_free_pred(pred);
+               return cnt;
+       }
 
-       r += sprintf(buf, "printf\n");
+       err = filter_add_subsystem_pred(system, pred);
+       if (err < 0) {
+               filter_free_subsystem_preds(system);
+               filter_free_pred(pred);
+               return err;
+       }
 
-       if (call->raw_init)
-               r += sprintf(buf+r, "raw\n");
+       *ppos += cnt;
 
-       return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+       return cnt;
 }
 
 static const struct seq_operations show_event_seq_ops = {
@@ -472,15 +641,26 @@ static const struct file_operations ftrace_enable_fops = {
        .write = event_enable_write,
 };
 
-static const struct file_operations ftrace_type_fops = {
+static const struct file_operations ftrace_event_format_fops = {
+       .open = tracing_open_generic,
+       .read = event_format_read,
+};
+
+static const struct file_operations ftrace_event_id_fops = {
+       .open = tracing_open_generic,
+       .read = event_id_read,
+};
+
+static const struct file_operations ftrace_event_filter_fops = {
        .open = tracing_open_generic,
-       .read = event_type_read,
-       .write = event_type_write,
+       .read = event_filter_read,
+       .write = event_filter_write,
 };
 
-static const struct file_operations ftrace_available_types_fops = {
+static const struct file_operations ftrace_subsystem_filter_fops = {
        .open = tracing_open_generic,
-       .read = event_available_types_read,
+       .read = subsystem_filter_read,
+       .write = subsystem_filter_write,
 };
 
 static struct dentry *event_trace_events_dir(void)
@@ -503,18 +683,13 @@ static struct dentry *event_trace_events_dir(void)
        return d_events;
 }
 
-struct event_subsystem {
-       struct list_head        list;
-       const char              *name;
-       struct dentry           *entry;
-};
-
 static LIST_HEAD(event_subsystems);
 
 static struct dentry *
 event_subsystem_dir(const char *name, struct dentry *d_events)
 {
        struct event_subsystem *system;
+       struct dentry *entry;
 
        /* First see if we did not already create this dir */
        list_for_each_entry(system, &event_subsystems, list) {
@@ -538,9 +713,24 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
                return d_events;
        }
 
-       system->name = name;
+       system->name = kstrdup(name, GFP_KERNEL);
+       if (!system->name) {
+               debugfs_remove(system->entry);
+               kfree(system);
+               return d_events;
+       }
+
        list_add(&system->list, &event_subsystems);
 
+       system->preds = NULL;
+       system->n_preds = 0;
+
+       entry = debugfs_create_file("filter", 0644, system->entry, system,
+                                   &ftrace_subsystem_filter_fops);
+       if (!entry)
+               pr_warning("Could not create debugfs "
+                          "'%s/filter' entry\n", name);
+
        return system->entry;
 }
 
@@ -554,7 +744,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
         * If the trace point header did not define TRACE_SYSTEM
         * then the system would be called "TRACE_SYSTEM".
         */
-       if (strcmp(call->system, "TRACE_SYSTEM") != 0)
+       if (strcmp(call->system, TRACE_SYSTEM) != 0)
                d_events = event_subsystem_dir(call->system, d_events);
 
        if (call->raw_init) {
@@ -566,9 +756,6 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
                }
        }
 
-       /* default the output to printf */
-       call->type = TRACE_EVENT_TYPE_PRINTF;
-
        call->dir = debugfs_create_dir(call->name, d_events);
        if (!call->dir) {
                pr_warning("Could not create debugfs "
@@ -576,36 +763,125 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
                return -1;
        }
 
-       entry = debugfs_create_file("enable", 0644, call->dir, call,
-                                   &ftrace_enable_fops);
-       if (!entry)
-               pr_warning("Could not create debugfs "
-                          "'%s/enable' entry\n", call->name);
+       if (call->regfunc)
+               entry = trace_create_file("enable", 0644, call->dir, call,
+                                         &ftrace_enable_fops);
 
-       /* Only let type be writable, if we can change it */
-       entry = debugfs_create_file("type",
-                                   call->raw_init ? 0644 : 0444,
-                                   call->dir, call,
-                                   &ftrace_type_fops);
-       if (!entry)
-               pr_warning("Could not create debugfs "
-                          "'%s/type' entry\n", call->name);
+       if (call->id)
+               entry = trace_create_file("id", 0444, call->dir, call,
+                                         &ftrace_event_id_fops);
 
-       entry = debugfs_create_file("available_types", 0444, call->dir, call,
-                                   &ftrace_available_types_fops);
-       if (!entry)
-               pr_warning("Could not create debugfs "
-                          "'%s/type' available_types\n", call->name);
+       if (call->define_fields) {
+               ret = call->define_fields();
+               if (ret < 0) {
+                       pr_warning("Could not initialize trace point"
+                                  " events/%s\n", call->name);
+                       return ret;
+               }
+               entry = trace_create_file("filter", 0644, call->dir, call,
+                                         &ftrace_event_filter_fops);
+       }
+
+       /* A trace may not want to export its format */
+       if (!call->show_format)
+               return 0;
+
+       entry = trace_create_file("format", 0444, call->dir, call,
+                                 &ftrace_event_format_fops);
+
+       return 0;
+}
+
+#define for_each_event(event, start, end)                      \
+       for (event = start;                                     \
+            (unsigned long)event < (unsigned long)end;         \
+            event++)
+
+#ifdef CONFIG_MODULES
+static void trace_module_add_events(struct module *mod)
+{
+       struct ftrace_event_call *call, *start, *end;
+       struct dentry *d_events;
+
+       start = mod->trace_events;
+       end = mod->trace_events + mod->num_trace_events;
+
+       if (start == end)
+               return;
+
+       d_events = event_trace_events_dir();
+       if (!d_events)
+               return;
+
+       for_each_event(call, start, end) {
+               /* The linker may leave blanks */
+               if (!call->name)
+                       continue;
+               call->mod = mod;
+               list_add(&call->list, &ftrace_events);
+               event_create_dir(call, d_events);
+       }
+}
+
+static void trace_module_remove_events(struct module *mod)
+{
+       struct ftrace_event_call *call, *p;
+
+       list_for_each_entry_safe(call, p, &ftrace_events, list) {
+               if (call->mod == mod) {
+                       if (call->enabled) {
+                               call->enabled = 0;
+                               call->unregfunc();
+                       }
+                       if (call->event)
+                               unregister_ftrace_event(call->event);
+                       debugfs_remove_recursive(call->dir);
+                       list_del(&call->list);
+               }
+       }
+}
+
+static int trace_module_notify(struct notifier_block *self,
+                              unsigned long val, void *data)
+{
+       struct module *mod = data;
+
+       mutex_lock(&event_mutex);
+       switch (val) {
+       case MODULE_STATE_COMING:
+               trace_module_add_events(mod);
+               break;
+       case MODULE_STATE_GOING:
+               trace_module_remove_events(mod);
+               break;
+       }
+       mutex_unlock(&event_mutex);
 
        return 0;
 }
+#else
+static int trace_module_notify(struct notifier_block *self,
+                              unsigned long val, void *data)
+{
+       return 0;
+}
+#endif /* CONFIG_MODULES */
+
+struct notifier_block trace_module_nb = {
+       .notifier_call = trace_module_notify,
+       .priority = 0,
+};
+
+extern struct ftrace_event_call __start_ftrace_events[];
+extern struct ftrace_event_call __stop_ftrace_events[];
 
 static __init int event_trace_init(void)
 {
-       struct ftrace_event_call *call = __start_ftrace_events;
+       struct ftrace_event_call *call;
        struct dentry *d_tracer;
        struct dentry *entry;
        struct dentry *d_events;
+       int ret;
 
        d_tracer = tracing_init_dentry();
        if (!d_tracer)
@@ -629,13 +905,18 @@ static __init int event_trace_init(void)
        if (!d_events)
                return 0;
 
-       events_for_each(call) {
+       for_each_event(call, __start_ftrace_events, __stop_ftrace_events) {
                /* The linker may leave blanks */
                if (!call->name)
                        continue;
+               list_add(&call->list, &ftrace_events);
                event_create_dir(call, d_events);
        }
 
+       ret = register_module_notifier(&trace_module_nb);
+       if (!ret)
+               pr_warning("Failed to register trace events module notifier\n");
+
        return 0;
 }
 fs_initcall(event_trace_init);