]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/trace/trace_events.c
tracing: Allow for modules to convert their enums to values
[karo-tx-linux.git] / kernel / trace / trace_events.c
index db54dda10ccc179f733db8fd8407cc402c11e409..a576bbe75577e9e3d6896a2bc54b9fd98c8407e5 100644 (file)
@@ -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 */