Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
[platform/adaptation/renesas_rcar/renesas_kernel.git] / kernel / trace / trace_events.c
index d57a772..64ec4d2 100644 (file)
 #include <linux/module.h>
 #include <linux/ctype.h>
 
-#include "trace.h"
+#include "trace_output.h"
 
 #define TRACE_SYSTEM "TRACE_SYSTEM"
 
 static DEFINE_MUTEX(event_mutex);
 
-#define events_for_each(event)                                         \
-       for (event = __start_ftrace_events;                             \
-            (unsigned long)event < (unsigned long)__stop_ftrace_events; \
-            event++)
-
-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;
+
+       field->offset = offset;
+       field->size = size;
+       list_add(&field->link, &call->fields);
+
+       return 0;
 
-       va_start(ap, fmt);
-       tracing_record_cmdline(current);
-       trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap);
-       va_end(ap);
+err:
+       if (field) {
+               kfree(field->name);
+               kfree(field->type);
+       }
+       kfree(field);
+
+       return -ENOMEM;
 }
 
 static void ftrace_clear_events(void)
@@ -59,22 +77,12 @@ 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;
        }
 }
@@ -110,9 +118,9 @@ static int ftrace_set_clr_event(char *buf, int set)
        }
 
        mutex_lock(&event_mutex);
-       events_for_each(call) {
+       for_each_event(call) {
 
-               if (!call->name)
+               if (!call->name || !call->regfunc)
                        continue;
 
                if (match &&
@@ -151,6 +159,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;
@@ -217,8 +229,20 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
 
        (*pos)++;
 
-       if ((unsigned long)call >= (unsigned long)__stop_ftrace_events)
-               return NULL;
+       for (;;) {
+               if ((unsigned long)call >= (unsigned long)__stop_ftrace_events)
+                       return NULL;
+
+               /*
+                * The ftrace subsystem is for showing formats only.
+                * They can not be enabled or disabled via the event files.
+                */
+               if (call->regfunc)
+                       break;
+
+               call++;
+               next = call;
+       }
 
        m->private = ++next;
 
@@ -300,7 +324,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";
@@ -329,6 +353,10 @@ 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:
@@ -346,143 +374,229 @@ 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;
 
-       if (call->type & TRACE_EVENT_TYPE_RAW)
-               r += sprintf(buf+r, "raw\n");
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       trace_seq_init(s);
 
-       return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+       /* 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);
+
+       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;
+       }
+
+       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;
 
-       if (!call->raw_init)
-               return -EPERM;
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
 
-       if (cnt >= sizeof(buf))
-               return -EINVAL;
+       trace_seq_init(s);
+       trace_seq_printf(s, "%d\n", call->id);
 
-       if (copy_from_user(&buf, ubuf, cnt))
-               return -EFAULT;
+       r = simple_read_from_buffer(ubuf, cnt, ppos,
+                                   s->buffer, s->len);
+       kfree(s);
+       return r;
+}
 
-       buf[cnt] = 0;
+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 (!strncmp(buf, "printf", 6) &&
-           (!buf[6] || isspace(buf[6]))) {
+       if (*ppos)
+               return 0;
 
-               call->type = TRACE_EVENT_TYPE_PRINTF;
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
 
-               /*
-                * If raw enabled, the disable it and enable
-                * printf type.
-                */
-               if (call->raw_enabled) {
-                       call->raw_enabled = 0;
-                       call->raw_unreg();
+       trace_seq_init(s);
 
-                       call->enabled = 1;
-                       call->regfunc();
-               }
+       filter_print_preds(call->preds, s);
+       r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
 
-       } else if (!strncmp(buf, "raw", 3) &&
-           (!buf[3] || isspace(buf[3]))) {
+       kfree(s);
 
-               call->type = TRACE_EVENT_TYPE_RAW;
+       return r;
+}
 
-               /*
-                * If printf enabled, the disable it and enable
-                * raw type.
-                */
-               if (call->enabled) {
-                       call->enabled = 0;
-                       call->unregfunc();
+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;
 
-                       call->raw_enabled = 1;
-                       call->raw_reg();
-               }
-       } else
+       if (cnt >= sizeof(buf))
                return -EINVAL;
 
-       *ppos += cnt;
+       if (copy_from_user(&buf, ubuf, cnt))
+               return -EFAULT;
 
-       return cnt;
-}
+       pred = kzalloc(sizeof(*pred), GFP_KERNEL);
+       if (!pred)
+               return -ENOMEM;
 
-static ssize_t
-event_available_types_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;
+       err = filter_parse(&pbuf, pred);
+       if (err < 0) {
+               filter_free_pred(pred);
+               return err;
+       }
 
-       r += sprintf(buf, "printf\n");
+       if (pred->clear) {
+               filter_free_preds(call);
+               filter_free_pred(pred);
+               return cnt;
+       }
+
+       if (filter_add_pred(call, pred)) {
+               filter_free_pred(pred);
+               return -EINVAL;
+       }
 
-       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 ssize_t
-event_format_read(struct file *filp, char __user *ubuf, size_t cnt,
-                 loff_t *ppos)
+subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
+                     loff_t *ppos)
 {
-       struct ftrace_event_call *call = filp->private_data;
+       struct event_subsystem *system = filp->private_data;
        struct trace_seq *s;
-       char *buf;
        int r;
 
+       if (*ppos)
+               return 0;
+
        s = kmalloc(sizeof(*s), GFP_KERNEL);
        if (!s)
                return -ENOMEM;
 
        trace_seq_init(s);
 
-       if (*ppos)
-               return 0;
+       filter_print_preds(system->preds, s);
+       r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
 
-       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;
-       }
-
-       r = simple_read_from_buffer(ubuf, cnt, ppos,
-                                   s->buffer, s->len);
- out:
        kfree(s);
+
        return r;
 }
 
+static ssize_t
+subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
+                      loff_t *ppos)
+{
+       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;
+
+       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;
+       }
+
+       if (filter_add_subsystem_pred(system, pred)) {
+               filter_free_subsystem_preds(system);
+               filter_free_pred(pred);
+               return -EINVAL;
+       }
+
+       *ppos += cnt;
+
+       return cnt;
+}
+
 static const struct seq_operations show_event_seq_ops = {
        .start = t_start,
        .next = t_next,
@@ -518,20 +632,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_type_read,
-       .write = event_type_write,
+       .read = event_format_read,
 };
 
-static const struct file_operations ftrace_available_types_fops = {
+static const struct file_operations ftrace_event_id_fops = {
        .open = tracing_open_generic,
-       .read = event_available_types_read,
+       .read = event_id_read,
 };
 
-static const struct file_operations ftrace_event_format_fops = {
+static const struct file_operations ftrace_event_filter_fops = {
        .open = tracing_open_generic,
-       .read = event_format_read,
+       .read = event_filter_read,
+       .write = event_filter_write,
+};
+
+static const struct file_operations ftrace_subsystem_filter_fops = {
+       .open = tracing_open_generic,
+       .read = subsystem_filter_read,
+       .write = subsystem_filter_write,
 };
 
 static struct dentry *event_trace_events_dir(void)
@@ -554,12 +674,6 @@ 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 *
@@ -592,6 +706,8 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
        system->name = name;
        list_add(&system->list, &event_subsystems);
 
+       system->preds = NULL;
+
        return system->entry;
 }
 
@@ -617,9 +733,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 "
@@ -627,26 +740,35 @@ 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 = 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);
+       }
 
-       /* 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 = debugfs_create_file("id", 0444, call->dir, call,
+                               &ftrace_event_id_fops);
+               if (!entry)
+                       pr_warning("Could not create debugfs '%s/id' entry\n",
+                                       call->name);
+       }
 
-       entry = debugfs_create_file("available_types", 0444, call->dir, call,
-                                   &ftrace_available_types_fops);
-       if (!entry)
-               pr_warning("Could not create debugfs "
-                          "'%s/available_types' entry\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 = debugfs_create_file("filter", 0644, call->dir, call,
+                                           &ftrace_event_filter_fops);
+               if (!entry)
+                       pr_warning("Could not create debugfs "
+                                  "'%s/filter' entry\n", call->name);
+       }
 
        /* A trace may not want to export its format */
        if (!call->show_format)
@@ -690,7 +812,7 @@ static __init int event_trace_init(void)
        if (!d_events)
                return 0;
 
-       events_for_each(call) {
+       for_each_event(call) {
                /* The linker may leave blanks */
                if (!call->name)
                        continue;