tracing/fprobe-event: Assume fprobe is a return event by $retval
authorMasami Hiramatsu (Google) <mhiramat@kernel.org>
Tue, 22 Aug 2023 16:26:42 +0000 (01:26 +0900)
committerMasami Hiramatsu (Google) <mhiramat@kernel.org>
Wed, 23 Aug 2023 00:41:32 +0000 (09:41 +0900)
Assume the fprobe event is a return event if there is $retval is
used in the probe's argument without %return. e.g.

echo 'f:myevent vfs_read $retval' >> dynamic_events

then 'myevent' is a return probe event.

Link: https://lore.kernel.org/all/169272160261.160970.13613040161560998787.stgit@devnote2/
Suggested-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Steven Rostedt (Google) <rostedt@goodmis.org>
kernel/trace/trace_fprobe.c
tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc

index 8f43f1f..8bfe23a 100644 (file)
@@ -898,6 +898,46 @@ static struct tracepoint *find_tracepoint(const char *tp_name)
        return data.tpoint;
 }
 
+static int parse_symbol_and_return(int argc, const char *argv[],
+                                  char **symbol, bool *is_return,
+                                  bool is_tracepoint)
+{
+       char *tmp = strchr(argv[1], '%');
+       int i;
+
+       if (tmp) {
+               int len = tmp - argv[1];
+
+               if (!is_tracepoint && !strcmp(tmp, "%return")) {
+                       *is_return = true;
+               } else {
+                       trace_probe_log_err(len, BAD_ADDR_SUFFIX);
+                       return -EINVAL;
+               }
+               *symbol = kmemdup_nul(argv[1], len, GFP_KERNEL);
+       } else
+               *symbol = kstrdup(argv[1], GFP_KERNEL);
+       if (!*symbol)
+               return -ENOMEM;
+
+       if (*is_return)
+               return 0;
+
+       /* If there is $retval, this should be a return fprobe. */
+       for (i = 2; i < argc; i++) {
+               tmp = strstr(argv[i], "$retval");
+               if (tmp && !isalnum(tmp[7]) && tmp[7] != '_') {
+                       *is_return = true;
+                       /*
+                        * NOTE: Don't check is_tracepoint here, because it will
+                        * be checked when the argument is parsed.
+                        */
+                       break;
+               }
+       }
+       return 0;
+}
+
 static int __trace_fprobe_create(int argc, const char *argv[])
 {
        /*
@@ -927,7 +967,7 @@ static int __trace_fprobe_create(int argc, const char *argv[])
        struct trace_fprobe *tf = NULL;
        int i, len, new_argc = 0, ret = 0;
        bool is_return = false;
-       char *symbol = NULL, *tmp = NULL;
+       char *symbol = NULL;
        const char *event = NULL, *group = FPROBE_EVENT_SYSTEM;
        const char **new_argv = NULL;
        int maxactive = 0;
@@ -983,20 +1023,10 @@ static int __trace_fprobe_create(int argc, const char *argv[])
        trace_probe_log_set_index(1);
 
        /* a symbol(or tracepoint) must be specified */
-       symbol = kstrdup(argv[1], GFP_KERNEL);
-       if (!symbol)
-               return -ENOMEM;
+       ret = parse_symbol_and_return(argc, argv, &symbol, &is_return, is_tracepoint);
+       if (ret < 0)
+               goto parse_error;
 
-       tmp = strchr(symbol, '%');
-       if (tmp) {
-               if (!is_tracepoint && !strcmp(tmp, "%return")) {
-                       *tmp = '\0';
-                       is_return = true;
-               } else {
-                       trace_probe_log_err(tmp - symbol, BAD_ADDR_SUFFIX);
-                       goto parse_error;
-               }
-       }
        if (!is_return && maxactive) {
                trace_probe_log_set_index(0);
                trace_probe_log_err(1, BAD_MAXACT_TYPE);
index 812f5b3..72563b2 100644 (file)
@@ -30,11 +30,11 @@ check_error 'f:^ vfs_read'          # NO_EVENT_NAME
 check_error 'f:foo/^12345678901234567890123456789012345678901234567890123456789012345 vfs_read'        # EVENT_TOO_LONG
 check_error 'f:foo/^bar.1 vfs_read'    # BAD_EVENT_NAME
 
-check_error 'f vfs_read ^$retval'      # RETVAL_ON_PROBE
 check_error 'f vfs_read ^$stack10000'  # BAD_STACK_NUM
 
 check_error 'f vfs_read ^$arg10000'    # BAD_ARG_NUM
 
+check_error 'f vfs_read $retval ^$arg1' # BAD_VAR
 check_error 'f vfs_read ^$none_var'    # BAD_VAR
 check_error 'f vfs_read ^'$REG         # BAD_VAR