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 1880a64..64ec4d2 100644 (file)
 
 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;
 
-       va_start(ap, fmt);
-       tracing_record_cmdline(current);
-       trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap);
-       va_end(ap);
+       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;
+
+err:
+       if (field) {
+               kfree(field->name);
+               kfree(field->type);
+       }
+       kfree(field);
+
+       return -ENOMEM;
 }
 
 static void ftrace_clear_events(void)
@@ -100,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 &&
@@ -141,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;
@@ -207,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;
 
@@ -319,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:
@@ -338,8 +376,8 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
 
 #undef FIELD
 #define FIELD(type, name)                                              \
-       #type, #name, (unsigned int)offsetof(typeof(field), name),      \
-               (unsigned int)sizeof(field.name)
+       #type, "common_" #name, offsetof(typeof(field), name),          \
+               sizeof(field.name)
 
 static int trace_write_header(struct trace_seq *s)
 {
@@ -347,11 +385,11 @@ static int trace_write_header(struct trace_seq *s)
 
        /* struct trace_entry */
        return trace_seq_printf(s,
-                               "\tfield:%s %s;\toffset:%u;\tsize:%u;\n"
-                               "\tfield:%s %s;\toffset:%u;\tsize:%u;\n"
-                               "\tfield:%s %s;\toffset:%u;\tsize:%u;\n"
-                               "\tfield:%s %s;\toffset:%u;\tsize:%u;\n"
-                               "\tfield:%s %s;\toffset:%u;\tsize:%u;\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"
+                               "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n"
                                "\n",
                                FIELD(unsigned char, type),
                                FIELD(unsigned char, flags),
@@ -369,15 +407,15 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt,
        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;
-
        /* If any of the first writes fail, so will the show_format. */
 
        trace_seq_printf(s, "name: %s\n", call->name);
@@ -403,6 +441,162 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt,
        return r;
 }
 
+static ssize_t
+event_id_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 (*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 (*ppos)
+               return 0;
+
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       trace_seq_init(s);
+
+       filter_print_preds(call->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;
+
+       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_preds(call);
+               filter_free_pred(pred);
+               return cnt;
+       }
+
+       if (filter_add_pred(call, pred)) {
+               filter_free_pred(pred);
+               return -EINVAL;
+       }
+
+       *ppos += cnt;
+
+       return cnt;
+}
+
+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;
+
+       if (*ppos)
+               return 0;
+
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       trace_seq_init(s);
+
+       filter_print_preds(system->preds, s);
+       r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
+
+       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,
@@ -417,6 +611,13 @@ static const struct seq_operations show_set_event_seq_ops = {
        .stop = t_stop,
 };
 
+static const struct file_operations ftrace_avail_fops = {
+       .open = ftrace_event_seq_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
 static const struct file_operations ftrace_set_event_fops = {
        .open = ftrace_event_seq_open,
        .read = seq_read,
@@ -436,6 +637,23 @@ static const struct file_operations ftrace_event_format_fops = {
        .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_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)
 {
        static struct dentry *d_tracer;
@@ -456,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 *
@@ -494,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;
 }
 
@@ -534,6 +748,28 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
                                   "'%s/enable' 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);
+       }
+
+       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)
                return 0;
@@ -558,6 +794,13 @@ static __init int event_trace_init(void)
        if (!d_tracer)
                return 0;
 
+       entry = debugfs_create_file("available_events", 0444, d_tracer,
+                                   (void *)&show_event_seq_ops,
+                                   &ftrace_avail_fops);
+       if (!entry)
+               pr_warning("Could not create debugfs "
+                          "'available_events' entry\n");
+
        entry = debugfs_create_file("set_event", 0644, d_tracer,
                                    (void *)&show_set_event_seq_ops,
                                    &ftrace_set_event_fops);
@@ -569,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;