From nobody Sat Feb 7 20:39:22 2026 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 19219C001B0 for ; Wed, 26 Jul 2023 13:01:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233391AbjGZNBD (ORCPT ); Wed, 26 Jul 2023 09:01:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59406 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233274AbjGZNBA (ORCPT ); Wed, 26 Jul 2023 09:01:00 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1267A2D44; Wed, 26 Jul 2023 06:00:48 -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 6426E617A5; Wed, 26 Jul 2023 13:00:47 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 15FF8C433C9; Wed, 26 Jul 2023 13:00:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1690376446; bh=6/a1q67iDYOczfgPLObhEGtGovnFdh2gQ07B23aoJXA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fucDkV3i/JZtWbxYYMVvij7YNSItWmXdXkG/Ug+28BV5MCTJfAeMRLM07GRacfn2I 4HYuElLW84b7l83hlUyuvFgZPNvDDIGWxHZWvtH5/yRtWyrVwg614qj2uZpFqM3oqF OsvCk47p2xKeDSBgfbt4oGAuyfOb7hGedCDNNr/eBaclLOZOxtvWRz6dJt0VU1kfkN J9b6vHnxyVAbzVpE5cZEwVTGihxWlnMQEF2+IiPmgMIZET/1AtsJxTSgRpmZHrlwK+ afzdfcubLrO/y7F8SLK2CCxfAuui8EcGYVba8StnMhsJUeBvg2jRkMGFAlmp9ioZxq QbskmxB+Wx+fQ== 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 v3 5/9] tracing/probes: Support BTF field access from $retval Date: Wed, 26 Jul 2023 22:00:42 +0900 Message-Id: <169037644255.607919.6218637643155663128.stgit@devnote2> X-Mailer: git-send-email 2.34.1 In-Reply-To: <169037639315.607919.2613476171148037242.stgit@devnote2> References: <169037639315.607919.2613476171148037242.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. --- kernel/trace/trace_probe.c | 180 +++++++++++++++++++---------------------= ---- kernel/trace/trace_probe.h | 1=20 2 files changed, 80 insertions(+), 101 deletions(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index f6b855de4256..65273fe76a9e 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -363,38 +363,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) @@ -402,6 +410,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; } @@ -517,7 +526,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)) @@ -533,17 +542,32 @@ 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; + } + /* 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; + } else + tid =3D 0; + code->op =3D FETCH_OP_RETVAL; + goto found; + } + + 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); @@ -554,7 +578,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; } @@ -579,7 +602,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; @@ -591,27 +614,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) { @@ -634,28 +636,13 @@ 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); } @@ -675,24 +662,23 @@ static int parse_btf_bitfield(struct fetch_insn **pco= de, return -EOPNOTSUPP; } =20 -#define parse_btf_arg_type(ctx) \ +#define find_fetch_type_from_btf_type(ctx) \ find_fetch_type(NULL, ctx->flags) =20 -#define parse_btf_retval_type(ctx) \ - find_fetch_type(NULL, ctx->flags) - -#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 @@ -711,18 +697,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"); @@ -824,7 +809,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 */ @@ -1121,12 +1106,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 */ @@ -1415,7 +1397,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) @@ -1426,9 +1407,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); @@ -1437,8 +1417,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; @@ -1453,7 +1431,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 */