]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/trace/trace_events.c
Merge tag 'cpumask-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[mirror_ubuntu-bionic-kernel.git] / kernel / trace / trace_events.c
index db54dda10ccc179f733db8fd8407cc402c11e409..7da1dfeb322e696c3352300982bfc0ad96fdd98f 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/workqueue.h>
 #include <linux/spinlock.h>
 #include <linux/kthread.h>
-#include <linux/debugfs.h>
+#include <linux/tracefs.h>
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/ctype.h>
@@ -480,7 +480,7 @@ static void remove_subsystem(struct ftrace_subsystem_dir *dir)
                return;
 
        if (!--dir->nr_events) {
-               debugfs_remove_recursive(dir->entry);
+               tracefs_remove_recursive(dir->entry);
                list_del(&dir->list);
                __put_system_dir(dir);
        }
@@ -499,7 +499,7 @@ static void remove_event_file_dir(struct ftrace_event_file *file)
                }
                spin_unlock(&dir->d_lock);
 
-               debugfs_remove_recursive(dir);
+               tracefs_remove_recursive(dir);
        }
 
        list_del(&file->list);
@@ -1526,7 +1526,7 @@ event_subsystem_dir(struct trace_array *tr, const char *name,
        } else
                __get_system(system);
 
-       dir->entry = debugfs_create_dir(name, parent);
+       dir->entry = tracefs_create_dir(name, parent);
        if (!dir->entry) {
                pr_warn("Failed to create system directory %s\n", name);
                __put_system(system);
@@ -1539,12 +1539,12 @@ event_subsystem_dir(struct trace_array *tr, const char *name,
        dir->subsystem = system;
        file->system = dir;
 
-       entry = debugfs_create_file("filter", 0644, dir->entry, dir,
+       entry = tracefs_create_file("filter", 0644, dir->entry, dir,
                                    &ftrace_subsystem_filter_fops);
        if (!entry) {
                kfree(system->filter);
                system->filter = NULL;
-               pr_warn("Could not create debugfs '%s/filter' entry\n", name);
+               pr_warn("Could not create tracefs '%s/filter' entry\n", name);
        }
 
        trace_create_file("enable", 0644, dir->entry, dir,
@@ -1585,9 +1585,9 @@ event_create_dir(struct dentry *parent, struct ftrace_event_file *file)
                d_events = parent;
 
        name = ftrace_event_name(call);
-       file->dir = debugfs_create_dir(name, d_events);
+       file->dir = tracefs_create_dir(name, d_events);
        if (!file->dir) {
-               pr_warn("Could not create debugfs '%s' directory\n", name);
+               pr_warn("Could not create tracefs '%s' directory\n", name);
                return -1;
        }
 
@@ -1704,6 +1704,125 @@ __register_event(struct ftrace_event_call *call, struct module *mod)
        return 0;
 }
 
+static char *enum_replace(char *ptr, struct trace_enum_map *map, int len)
+{
+       int rlen;
+       int elen;
+
+       /* Find the length of the enum value as a string */
+       elen = snprintf(ptr, 0, "%ld", map->enum_value);
+       /* Make sure there's enough room to replace the string with the value */
+       if (len < elen)
+               return NULL;
+
+       snprintf(ptr, elen + 1, "%ld", map->enum_value);
+
+       /* Get the rest of the string of ptr */
+       rlen = strlen(ptr + len);
+       memmove(ptr + elen, ptr + len, rlen);
+       /* Make sure we end the new string */
+       ptr[elen + rlen] = 0;
+
+       return ptr + elen;
+}
+
+static void update_event_printk(struct ftrace_event_call *call,
+                               struct trace_enum_map *map)
+{
+       char *ptr;
+       int quote = 0;
+       int len = strlen(map->enum_string);
+
+       for (ptr = call->print_fmt; *ptr; ptr++) {
+               if (*ptr == '\\') {
+                       ptr++;
+                       /* paranoid */
+                       if (!*ptr)
+                               break;
+                       continue;
+               }
+               if (*ptr == '"') {
+                       quote ^= 1;
+                       continue;
+               }
+               if (quote)
+                       continue;
+               if (isdigit(*ptr)) {
+                       /* skip numbers */
+                       do {
+                               ptr++;
+                               /* Check for alpha chars like ULL */
+                       } while (isalnum(*ptr));
+                       /*
+                        * A number must have some kind of delimiter after
+                        * it, and we can ignore that too.
+                        */
+                       continue;
+               }
+               if (isalpha(*ptr) || *ptr == '_') {
+                       if (strncmp(map->enum_string, ptr, len) == 0 &&
+                           !isalnum(ptr[len]) && ptr[len] != '_') {
+                               ptr = enum_replace(ptr, map, len);
+                               /* Hmm, enum string smaller than value */
+                               if (WARN_ON_ONCE(!ptr))
+                                       return;
+                               /*
+                                * No need to decrement here, as enum_replace()
+                                * returns the pointer to the character passed
+                                * the enum, and two enums can not be placed
+                                * back to back without something in between.
+                                * We can skip that something in between.
+                                */
+                               continue;
+                       }
+               skip_more:
+                       do {
+                               ptr++;
+                       } while (isalnum(*ptr) || *ptr == '_');
+                       /*
+                        * If what comes after this variable is a '.' or
+                        * '->' then we can continue to ignore that string.
+                        */
+                       if (*ptr == '.' || (ptr[0] == '-' && ptr[1] == '>')) {
+                               ptr += *ptr == '.' ? 1 : 2;
+                               goto skip_more;
+                       }
+                       /*
+                        * Once again, we can skip the delimiter that came
+                        * after the string.
+                        */
+                       continue;
+               }
+       }
+}
+
+void trace_event_enum_update(struct trace_enum_map **map, int len)
+{
+       struct ftrace_event_call *call, *p;
+       const char *last_system = NULL;
+       int last_i;
+       int i;
+
+       down_write(&trace_event_sem);
+       list_for_each_entry_safe(call, p, &ftrace_events, list) {
+               /* events are usually grouped together with systems */
+               if (!last_system || call->class->system != last_system) {
+                       last_i = 0;
+                       last_system = call->class->system;
+               }
+
+               for (i = last_i; i < len; i++) {
+                       if (call->class->system == map[i]->system) {
+                               /* Save the first system if need be */
+                               if (!last_i)
+                                       last_i = i;
+                               update_event_printk(call, map[i]);
+                       }
+               }
+       }
+       up_write(&trace_event_sem);
+}
+
 static struct ftrace_event_file *
 trace_create_new_event(struct ftrace_event_call *call,
                       struct trace_array *tr)
@@ -1915,7 +2034,7 @@ static int trace_module_notify(struct notifier_block *self,
 
 static struct notifier_block trace_module_nb = {
        .notifier_call = trace_module_notify,
-       .priority = 0,
+       .priority = 1, /* higher than trace.c module notify */
 };
 #endif /* CONFIG_MODULES */
 
@@ -2228,7 +2347,7 @@ static inline int register_event_cmds(void) { return 0; }
 /*
  * The top level array has already had its ftrace_event_file
  * descriptors created in order to allow for early events to
- * be recorded. This function is called after the debugfs has been
+ * be recorded. This function is called after the tracefs has been
  * initialized, and we now have to create the files associated
  * to the events.
  */
@@ -2311,16 +2430,16 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
        struct dentry *d_events;
        struct dentry *entry;
 
-       entry = debugfs_create_file("set_event", 0644, parent,
+       entry = tracefs_create_file("set_event", 0644, parent,
                                    tr, &ftrace_set_event_fops);
        if (!entry) {
-               pr_warn("Could not create debugfs 'set_event' entry\n");
+               pr_warn("Could not create tracefs 'set_event' entry\n");
                return -ENOMEM;
        }
 
-       d_events = debugfs_create_dir("events", parent);
+       d_events = tracefs_create_dir("events", parent);
        if (!d_events) {
-               pr_warn("Could not create debugfs 'events' directory\n");
+               pr_warn("Could not create tracefs 'events' directory\n");
                return -ENOMEM;
        }
 
@@ -2412,7 +2531,7 @@ int event_trace_del_tracer(struct trace_array *tr)
 
        down_write(&trace_event_sem);
        __trace_remove_event_dirs(tr);
-       debugfs_remove_recursive(tr->event_dir);
+       tracefs_remove_recursive(tr->event_dir);
        up_write(&trace_event_sem);
 
        tr->event_dir = NULL;
@@ -2534,10 +2653,10 @@ static __init int event_trace_init(void)
        if (IS_ERR(d_tracer))
                return 0;
 
-       entry = debugfs_create_file("available_events", 0444, d_tracer,
+       entry = tracefs_create_file("available_events", 0444, d_tracer,
                                    tr, &ftrace_avail_fops);
        if (!entry)
-               pr_warn("Could not create debugfs 'available_events' entry\n");
+               pr_warn("Could not create tracefs 'available_events' entry\n");
 
        if (trace_define_common_fields())
                pr_warn("tracing: Failed to allocate common fields");