user_events: Handle matching arguments from dyn_events
authorBeau Belgrave <beaub@linux.microsoft.com>
Tue, 18 Jan 2022 20:43:17 +0000 (12:43 -0800)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Fri, 11 Feb 2022 03:37:16 +0000 (22:37 -0500)
Ensures that when dynamic events requests a match with arguments that
they match what is in the user_event.

Link: https://lkml.kernel.org/r/20220118204326.2169-4-beaub@linux.microsoft.com
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Beau Belgrave <beaub@linux.microsoft.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
kernel/trace/trace_events_user.c

index ddc5c3cf1bf86c53a595ca62fb428560e511cb13..a6794cb1f5862f932f385bd8859564340951a587 100644 (file)
@@ -39,6 +39,7 @@
 #define MAX_EVENT_DESC 512
 #define EVENT_NAME(user_event) ((user_event)->tracepoint.name)
 #define MAX_FIELD_ARRAY_SIZE 1024
+#define MAX_FIELD_ARG_NAME 256
 
 static char *register_page_data;
 
@@ -700,13 +701,87 @@ static int user_event_free(struct dyn_event *ev)
        return destroy_user_event(user);
 }
 
+static bool user_field_match(struct ftrace_event_field *field, int argc,
+                            const char **argv, int *iout)
+{
+       char *field_name, *arg_name;
+       int len, pos, i = *iout;
+       bool colon = false, match = false;
+
+       if (i >= argc)
+               return false;
+
+       len = MAX_FIELD_ARG_NAME;
+       field_name = kmalloc(len, GFP_KERNEL);
+       arg_name = kmalloc(len, GFP_KERNEL);
+
+       if (!arg_name || !field_name)
+               goto out;
+
+       pos = 0;
+
+       for (; i < argc; ++i) {
+               if (i != *iout)
+                       pos += snprintf(arg_name + pos, len - pos, " ");
+
+               pos += snprintf(arg_name + pos, len - pos, argv[i]);
+
+               if (strchr(argv[i], ';')) {
+                       ++i;
+                       colon = true;
+                       break;
+               }
+       }
+
+       pos = 0;
+
+       pos += snprintf(field_name + pos, len - pos, field->type);
+       pos += snprintf(field_name + pos, len - pos, " ");
+       pos += snprintf(field_name + pos, len - pos, field->name);
+
+       if (colon)
+               pos += snprintf(field_name + pos, len - pos, ";");
+
+       *iout = i;
+
+       match = strcmp(arg_name, field_name) == 0;
+out:
+       kfree(arg_name);
+       kfree(field_name);
+
+       return match;
+}
+
+static bool user_fields_match(struct user_event *user, int argc,
+                             const char **argv)
+{
+       struct ftrace_event_field *field, *next;
+       struct list_head *head = &user->fields;
+       int i = 0;
+
+       list_for_each_entry_safe_reverse(field, next, head, link)
+               if (!user_field_match(field, argc, argv, &i))
+                       return false;
+
+       if (i != argc)
+               return false;
+
+       return true;
+}
+
 static bool user_event_match(const char *system, const char *event,
                             int argc, const char **argv, struct dyn_event *ev)
 {
        struct user_event *user = container_of(ev, struct user_event, devent);
+       bool match;
 
-       return strcmp(EVENT_NAME(user), event) == 0 &&
+       match = strcmp(EVENT_NAME(user), event) == 0 &&
                (!system || strcmp(system, USER_EVENTS_SYSTEM) == 0);
+
+       if (match && argc > 0)
+               match = user_fields_match(user, argc, argv);
+
+       return match;
 }
 
 static struct dyn_event_operations user_event_dops = {