tracing/probes: Add BTF retval type support
authorMasami Hiramatsu (Google) <mhiramat@kernel.org>
Tue, 6 Jun 2023 12:39:56 +0000 (21:39 +0900)
committerMasami Hiramatsu (Google) <mhiramat@kernel.org>
Tue, 6 Jun 2023 12:39:56 +0000 (21:39 +0900)
Check the target function has non-void retval type and set the correct
fetch type if user doesn't specify it.
If the function returns void, $retval is rejected as below;

 # echo 'f unregister_kprobes%return $retval' >> dynamic_events
sh: write error: No such file or directory
 # cat error_log
[   37.488397] trace_fprobe: error: This function returns 'void' type
  Command: f unregister_kprobes%return $retval
                                       ^
Link: https://lore.kernel.org/all/168507476195.913472.16290308831790216609.stgit@mhiramat.roam.corp.google.com/
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
kernel/trace/trace_probe.c
kernel/trace/trace_probe.h

index 7216435..ba1c6e0 100644 (file)
@@ -371,15 +371,13 @@ static const char *type_from_btf_id(struct btf *btf, s32 id)
        return NULL;
 }
 
-static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr,
-                                                  bool tracepoint)
+static const struct btf_type *find_btf_func_proto(const char *funcname)
 {
        struct btf *btf = traceprobe_get_btf();
-       const struct btf_param *param;
        const struct btf_type *t;
        s32 id;
 
-       if (!btf || !funcname || !nr)
+       if (!btf || !funcname)
                return ERR_PTR(-EINVAL);
 
        id = btf_find_by_name_kind(btf, funcname, BTF_KIND_FUNC);
@@ -396,6 +394,22 @@ static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr
        if (!btf_type_is_func_proto(t))
                return ERR_PTR(-ENOENT);
 
+       return t;
+}
+
+static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr,
+                                                  bool tracepoint)
+{
+       const struct btf_param *param;
+       const struct btf_type *t;
+
+       if (!funcname || !nr)
+               return ERR_PTR(-EINVAL);
+
+       t = find_btf_func_proto(funcname);
+       if (IS_ERR(t))
+               return (const struct btf_param *)t;
+
        *nr = btf_type_vlen(t);
        param = (const struct btf_param *)(t + 1);
 
@@ -462,6 +476,32 @@ static const struct fetch_type *parse_btf_arg_type(int arg_idx,
        return find_fetch_type(typestr, ctx->flags);
 }
 
+static const struct fetch_type *parse_btf_retval_type(
+                                       struct traceprobe_parse_context *ctx)
+{
+       struct btf *btf = traceprobe_get_btf();
+       const char *typestr = NULL;
+       const struct btf_type *t;
+
+       if (btf && ctx->funcname) {
+               t = find_btf_func_proto(ctx->funcname);
+               if (!IS_ERR(t))
+                       typestr = type_from_btf_id(btf, t->type);
+       }
+
+       return find_fetch_type(typestr, ctx->flags);
+}
+
+static bool is_btf_retval_void(const char *funcname)
+{
+       const struct btf_type *t;
+
+       t = find_btf_func_proto(funcname);
+       if (IS_ERR(t))
+               return false;
+
+       return t->type == 0;
+}
 #else
 static struct btf *traceprobe_get_btf(void)
 {
@@ -480,8 +520,15 @@ static int parse_btf_arg(const char *varname, struct fetch_insn *code,
        trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
        return -EOPNOTSUPP;
 }
+
 #define parse_btf_arg_type(idx, ctx)           \
        find_fetch_type(NULL, ctx->flags)
+
+#define parse_btf_retval_type(ctx)             \
+       find_fetch_type(NULL, ctx->flags)
+
+#define is_btf_retval_void(funcname)   (false)
+
 #endif
 
 #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
@@ -512,6 +559,11 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
 
        if (strcmp(arg, "retval") == 0) {
                if (ctx->flags & TPARG_FL_RETURN) {
+                       if ((ctx->flags & TPARG_FL_KERNEL) &&
+                           is_btf_retval_void(ctx->funcname)) {
+                               err = TP_ERR_NO_RETVAL;
+                               goto inval;
+                       }
                        code->op = FETCH_OP_RETVAL;
                        return 0;
                }
@@ -912,9 +964,12 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
                goto fail;
 
        /* Update storing type if BTF is available */
-       if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) &&
-           !t && code->op == FETCH_OP_ARG)
-               parg->type = parse_btf_arg_type(code->param, ctx);
+       if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && !t) {
+               if (code->op == FETCH_OP_ARG)
+                       parg->type = parse_btf_arg_type(code->param, ctx);
+               else if (code->op == FETCH_OP_RETVAL)
+                       parg->type = parse_btf_retval_type(ctx);
+       }
 
        ret = -EINVAL;
        /* Store operation */
index e7fa2f2..01ea148 100644 (file)
@@ -449,6 +449,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
        C(BAD_EVENT_NAME,       "Event name must follow the same rules as C identifiers"), \
        C(EVENT_EXIST,          "Given group/event name is already used by another event"), \
        C(RETVAL_ON_PROBE,      "$retval is not available on probe"),   \
+       C(NO_RETVAL,            "This function returns 'void' type"),   \
        C(BAD_STACK_NUM,        "Invalid stack number"),                \
        C(BAD_ARG_NUM,          "Invalid argument number"),             \
        C(BAD_VAR,              "Invalid $-valiable specified"),        \