From nobody Thu Sep 11 19:27:50 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 138D9C0015E for ; Mon, 7 Aug 2023 02:56:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229919AbjHGC4A (ORCPT ); Sun, 6 Aug 2023 22:56:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48542 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229853AbjHGCz6 (ORCPT ); Sun, 6 Aug 2023 22:55:58 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BC0A919BC; Sun, 6 Aug 2023 19:55:27 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id CEE2E61347; Mon, 7 Aug 2023 02:55:23 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 37FBBC433C7; Mon, 7 Aug 2023 02:55:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1691376923; bh=oGPU2qozXtuEf14iocpSzniQ83hSVXHfLyfBmI0OE2A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ih0krtd4eAsAeggMCajLm9mLbVSl0IYn1TbR+YCDNaNziq7ZB+tiNA4GajJUvnkYA I84YsQ9fG+/Pu8CYmkhcI1wbrexZww6Nxw0sV4rmwVoIA2wdGO4My9FtiVX199Jz8c 5hZS5FWr8GdKwCXHgosnhLqq6xaD4v9Wng0Xzj0vOPFgJZeXv881VUojhhrdtQbMWQ c6CeGPbaU+0XeUv20F0CtqgDaAkBQhfLjSAvKqdLlQOjDbC9eu4h9jQpy/CWHw2rNt mXPpNj+3Y2QLfeosuo9+Exrb14Qmt/+xac+BJbYptbNHmGrhHqeFQcarR0dBgFfl3G 3PUNdGlHQA34Q== From: "Masami Hiramatsu (Google)" To: linux-trace-kernel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Steven Rostedt , mhiramat@kernel.org, Martin KaFai Lau , bpf@vger.kernel.org, Sven Schnelle , Alexei Starovoitov Subject: [PATCH v5 5/9] tracing/probes: Support BTF field access from $retval Date: Mon, 7 Aug 2023 11:55:18 +0900 Message-Id: <169137691866.271367.17475776112000287816.stgit@devnote2> X-Mailer: git-send-email 2.34.1 In-Reply-To: <169137686814.271367.11218568219311636206.stgit@devnote2> References: <169137686814.271367.11218568219311636206.stgit@devnote2> User-Agent: StGit/0.19 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Masami Hiramatsu (Google) Support BTF argument on '$retval' for function return events including kretprobe and fprobe for accessing the return value. This also allows user to access its fields if the return value is a pointer of a data structure. E.g. # echo 'f getname_flags%return +0($retval->name):string' \ > dynamic_events # echo 1 > events/fprobes/getname_flags__exit/enable # ls > /dev/null # head -n 40 trace | tail ls-87 [000] ...1. 8067.616101: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./function_profile_enabled" ls-87 [000] ...1. 8067.616108: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./trace_stat" ls-87 [000] ...1. 8067.616115: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./set_graph_notrace" ls-87 [000] ...1. 8067.616122: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./set_graph_function" ls-87 [000] ...1. 8067.616129: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./set_ftrace_notrace" ls-87 [000] ...1. 8067.616135: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./set_ftrace_filter" ls-87 [000] ...1. 8067.616143: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./touched_functions" ls-87 [000] ...1. 8067.616237: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./enabled_functions" ls-87 [000] ...1. 8067.616245: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./available_filter_functions" ls-87 [000] ...1. 8067.616253: getname_flags__exit: (vf= s_fstatat+0x3c/0x70 <- getname_flags) arg1=3D"./set_ftrace_notrace_pid" Signed-off-by: Masami Hiramatsu (Google) --- Changes in v2: - Use '$retval' instead of 'retval' because it is confusing. Changes in v3: - Introduce query_btf_context() to cache the btf related data (function prototype) for using common field analyzing code with function parameters. Changes in v3.1: - Return int error code from query_btf_context() if !CONFIG_PROBE_EVENTS_= BTF_ARGS Changes in v4: - Fix wrong BTF access if query_btf_context() is failed in parse_btf_arg(= ). - Return error if $retval accesses a field but BTF is not found. --- kernel/trace/trace_probe.c | 187 ++++++++++++++++++++--------------------= ---- kernel/trace/trace_probe.h | 1=20 2 files changed, 86 insertions(+), 102 deletions(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 821f43e5c52b..7345e1af4db2 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -364,38 +364,46 @@ static const char *fetch_type_from_btf_type(struct bt= f *btf, return NULL; } =20 -static const struct btf_param *find_btf_func_param(const char *funcname, s= 32 *nr, - struct btf **btf_p, bool tracepoint) +static int query_btf_context(struct traceprobe_parse_context *ctx) { const struct btf_param *param; - const struct btf_type *t; + const struct btf_type *type; struct btf *btf; + s32 nr; =20 - if (!funcname || !nr) - return ERR_PTR(-EINVAL); + if (ctx->btf) + return 0; =20 - t =3D btf_find_func_proto(funcname, &btf); - if (!t) - return (const struct btf_param *)t; + if (!ctx->funcname) + return -EINVAL; =20 - param =3D btf_get_func_param(t, nr); - if (IS_ERR_OR_NULL(param)) - goto err; + type =3D btf_find_func_proto(ctx->funcname, &btf); + if (!type) + return -ENOENT; =20 - /* Hide the first 'data' argument of tracepoint */ - if (tracepoint) { - (*nr)--; - param++; + ctx->btf =3D btf; + ctx->proto =3D type; + + /* ctx->params is optional, since func(void) will not have params. */ + nr =3D 0; + param =3D btf_get_func_param(type, &nr); + if (!IS_ERR_OR_NULL(param)) { + /* Hide the first 'data' argument of tracepoint */ + if (ctx->flags & TPARG_FL_TPOINT) { + nr--; + param++; + } } =20 - if (*nr > 0) { - *btf_p =3D btf; - return param; + if (nr > 0) { + ctx->nr_params =3D nr; + ctx->params =3D param; + } else { + ctx->nr_params =3D 0; + ctx->params =3D NULL; } =20 -err: - btf_put(btf); - return NULL; + return 0; } =20 static void clear_btf_context(struct traceprobe_parse_context *ctx) @@ -403,6 +411,7 @@ static void clear_btf_context(struct traceprobe_parse_c= ontext *ctx) if (ctx->btf) { btf_put(ctx->btf); ctx->btf =3D NULL; + ctx->proto =3D NULL; ctx->params =3D NULL; ctx->nr_params =3D 0; } @@ -522,7 +531,7 @@ static int parse_btf_arg(char *varname, const struct btf_param *params; const struct btf_type *type; char *field =3D NULL; - int i, is_ptr; + int i, is_ptr, ret; u32 tid; =20 if (WARN_ON_ONCE(!ctx->funcname)) @@ -538,17 +547,37 @@ static int parse_btf_arg(char *varname, return -EOPNOTSUPP; } =20 - if (!ctx->params) { - params =3D find_btf_func_param(ctx->funcname, - &ctx->nr_params, &ctx->btf, - ctx->flags & TPARG_FL_TPOINT); - if (IS_ERR_OR_NULL(params)) { + if (ctx->flags & TPARG_FL_RETURN) { + if (strcmp(varname, "$retval") !=3D 0) { + trace_probe_log_err(ctx->offset, NO_BTFARG); + return -ENOENT; + } + code->op =3D FETCH_OP_RETVAL; + /* Check whether the function return type is not void */ + if (query_btf_context(ctx) =3D=3D 0) { + if (ctx->proto->type =3D=3D 0) { + trace_probe_log_err(ctx->offset, NO_RETVAL); + return -ENOENT; + } + tid =3D ctx->proto->type; + goto found; + } + if (field) { + trace_probe_log_err(ctx->offset + field - varname, + NO_BTF_ENTRY); + return -ENOENT; + } + return 0; + } + + if (!ctx->btf) { + ret =3D query_btf_context(ctx); + if (ret < 0 || ctx->nr_params =3D=3D 0) { trace_probe_log_err(ctx->offset, NO_BTF_ENTRY); return PTR_ERR(params); } - ctx->params =3D params; - } else - params =3D ctx->params; + } + params =3D ctx->params; =20 for (i =3D 0; i < ctx->nr_params; i++) { const char *name =3D btf_name_by_offset(ctx->btf, params[i].name_off); @@ -559,7 +588,6 @@ static int parse_btf_arg(char *varname, code->param =3D i + 1; else code->param =3D i; - tid =3D params[i].type; goto found; } @@ -584,7 +612,7 @@ static int parse_btf_arg(char *varname, return 0; } =20 -static const struct fetch_type *parse_btf_arg_type( +static const struct fetch_type *find_fetch_type_from_btf_type( struct traceprobe_parse_context *ctx) { struct btf *btf =3D ctx->btf; @@ -596,27 +624,6 @@ static const struct fetch_type *parse_btf_arg_type( return find_fetch_type(typestr, ctx->flags); } =20 -static const struct fetch_type *parse_btf_retval_type( - struct traceprobe_parse_context *ctx) -{ - const char *typestr =3D NULL; - const struct btf_type *type; - struct btf *btf; - - if (ctx->funcname) { - /* Do not use ctx->btf, because it must be used with ctx->param */ - type =3D btf_find_func_proto(ctx->funcname, &btf); - if (type) { - type =3D btf_type_skip_modifiers(btf, type->type, NULL); - if (!IS_ERR_OR_NULL(type)) - typestr =3D fetch_type_from_btf_type(btf, type, ctx); - btf_put(btf); - } - } - - return find_fetch_type(typestr, ctx->flags); -} - static int parse_btf_bitfield(struct fetch_insn **pcode, struct traceprobe_parse_context *ctx) { @@ -639,30 +646,15 @@ static int parse_btf_bitfield(struct fetch_insn **pco= de, return 0; } =20 -static bool is_btf_retval_void(const char *funcname) -{ - const struct btf_type *t; - struct btf *btf; - bool ret; - - t =3D btf_find_func_proto(funcname, &btf); - if (!t) - return false; - - ret =3D (t->type =3D=3D 0); - btf_put(btf); - return ret; -} #else static void clear_btf_context(struct traceprobe_parse_context *ctx) { ctx->btf =3D NULL; } =20 -static const struct btf_param *find_btf_func_param(const char *funcname, s= 32 *nr, - struct btf **btf_p, bool tracepoint) +static int query_btf_context(struct traceprobe_parse_context *ctx) { - return ERR_PTR(-EOPNOTSUPP); + return -EOPNOTSUPP; } =20 static int parse_btf_arg(char *varname, @@ -680,24 +672,23 @@ static int parse_btf_bitfield(struct fetch_insn **pco= de, return -EOPNOTSUPP; } =20 -#define parse_btf_arg_type(ctx) \ - find_fetch_type(NULL, ctx->flags) - -#define parse_btf_retval_type(ctx) \ +#define find_fetch_type_from_btf_type(ctx) \ find_fetch_type(NULL, ctx->flags) =20 -#define is_btf_retval_void(funcname) (false) - #endif =20 #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) =20 -static int parse_probe_vars(char *arg, const struct fetch_type *t, - struct fetch_insn *code, +/* Parse $vars. @orig_arg points '$', which syncs to @ctx->offset */ +static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, + struct fetch_insn **pcode, + struct fetch_insn *end, struct traceprobe_parse_context *ctx) { - unsigned long param; + struct fetch_insn *code =3D *pcode; int err =3D TP_ERR_BAD_VAR; + char *arg =3D orig_arg + 1; + unsigned long param; int ret =3D 0; int len; =20 @@ -716,18 +707,17 @@ static int parse_probe_vars(char *arg, const struct f= etch_type *t, goto inval; } =20 - if (strcmp(arg, "retval") =3D=3D 0) { - if (ctx->flags & TPARG_FL_RETURN) { - if ((ctx->flags & TPARG_FL_KERNEL) && - is_btf_retval_void(ctx->funcname)) { - err =3D TP_ERR_NO_RETVAL; - goto inval; - } + if (str_has_prefix(arg, "retval")) { + if (!(ctx->flags & TPARG_FL_RETURN)) { + err =3D TP_ERR_RETVAL_ON_PROBE; + goto inval; + } + if (!(ctx->flags & TPARG_FL_KERNEL) || + !IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS)) { code->op =3D FETCH_OP_RETVAL; return 0; } - err =3D TP_ERR_RETVAL_ON_PROBE; - goto inval; + return parse_btf_arg(orig_arg, pcode, end, ctx); } =20 len =3D str_has_prefix(arg, "stack"); @@ -829,7 +819,7 @@ parse_probe_arg(char *arg, const struct fetch_type *typ= e, =20 switch (arg[0]) { case '$': - ret =3D parse_probe_vars(arg + 1, type, code, ctx); + ret =3D parse_probe_vars(arg, type, pcode, end, ctx); break; =20 case '%': /* named register */ @@ -1126,12 +1116,9 @@ static int traceprobe_parse_probe_arg_body(const cha= r *argv, ssize_t *size, goto fail; =20 /* Update storing type if BTF is available */ - if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && !t) { - if (ctx->last_type) - parg->type =3D parse_btf_arg_type(ctx); - else if (ctx->flags & TPARG_FL_RETURN) - parg->type =3D parse_btf_retval_type(ctx); - } + if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && + !t && ctx->last_type) + parg->type =3D find_fetch_type_from_btf_type(ctx); =20 ret =3D -EINVAL; /* Store operation */ @@ -1420,7 +1407,6 @@ const char **traceprobe_expand_meta_args(int argc, co= nst char *argv[], const struct btf_param *params =3D NULL; int i, j, n, used, ret, args_idx =3D -1; const char **new_argv =3D NULL; - int nr_params; =20 ret =3D argv_has_var_arg(argc, argv, &args_idx, ctx); if (ret < 0) @@ -1431,9 +1417,8 @@ const char **traceprobe_expand_meta_args(int argc, co= nst char *argv[], return NULL; } =20 - params =3D find_btf_func_param(ctx->funcname, &nr_params, &ctx->btf, - ctx->flags & TPARG_FL_TPOINT); - if (IS_ERR_OR_NULL(params)) { + ret =3D query_btf_context(ctx); + if (ret < 0 || ctx->nr_params =3D=3D 0) { if (args_idx !=3D -1) { /* $arg* requires BTF info */ trace_probe_log_err(0, NOSUP_BTFARG); @@ -1442,8 +1427,6 @@ const char **traceprobe_expand_meta_args(int argc, co= nst char *argv[], *new_argc =3D argc; return NULL; } - ctx->params =3D params; - ctx->nr_params =3D nr_params; =20 if (args_idx >=3D 0) *new_argc =3D argc + ctx->nr_params - 1; @@ -1458,7 +1441,7 @@ const char **traceprobe_expand_meta_args(int argc, co= nst char *argv[], for (i =3D 0, j =3D 0; i < argc; i++) { trace_probe_log_set_index(i + 2); if (i =3D=3D args_idx) { - for (n =3D 0; n < nr_params; n++) { + for (n =3D 0; n < ctx->nr_params; n++) { ret =3D sprint_nth_btf_arg(n, "", buf + used, bufsize - used, ctx); if (ret < 0) diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 6111f1ffca6c..9184c84833f8 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -385,6 +385,7 @@ struct traceprobe_parse_context { struct trace_event_call *event; /* BTF related parameters */ const char *funcname; /* Function name in BTF */ + const struct btf_type *proto; /* Prototype of the function */ const struct btf_param *params; /* Parameter of the function */ s32 nr_params; /* The number of the parameters */ struct btf *btf; /* The BTF to be used */