Received: by 2002:a05:6358:c692:b0:131:369:b2a3 with SMTP id fe18csp1040087rwb; Wed, 26 Jul 2023 06:50:20 -0700 (PDT) X-Google-Smtp-Source: APBJJlGBxT0ItvcW5nr/l9WpFUDjo0QdtTD+PdvmNJXqUj6RgBqGTzW1CrgjX3pcosxz99D3ISHP X-Received: by 2002:a05:6402:1489:b0:522:30cc:a1f0 with SMTP id e9-20020a056402148900b0052230cca1f0mr1710402edv.14.1690379420525; Wed, 26 Jul 2023 06:50:20 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690379420; cv=none; d=google.com; s=arc-20160816; b=b3C8j9gnLtAWBgVBoRr1rPJiq+8xYTp5/kxsZ+szM4rR1r1tiIoJZZplPP97swZEdN 8sh/aleQ/YzyPvpA+3urXzHObmnt4G2W5cV5mSSFOtV2fd21sMiomvFGshsEAKyJPdio puAqKSLsaoK7qrMqAutG0CZgg0AAXYFZgbmx3Z5qJMcJJY8kmljVIIWMKWnhF8LTt9Lq j9H2XL1WLyCzt2OdQXpBwFvgm+bjAbyBeMIx5hfYXzAycNVgl1HoX50e1u12TFZfEtT5 75dexsTl0zZb4w4unWHuvtm6DX3FPjdLTAVDxVM0tG6jRKgZdy2HKneB8JmKnxNvnrff iZ8Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=PW0q0eXSf2twh7CLnf74Cq2sv5QF55iO5zuctcXcHFI=; fh=raxTTJvC+dSJYbgfX5JG5gvrVjxTZXczN0HF8iDayLY=; b=PlnbaVd5nPjIjfSXUZKM/+vv0rrRbSqgXu/pUGEtNR2pzzpT8lAICiw2ALoJyYvgoX Xf/b1JaGdA3osj2k58X8d9IdluDRxpWYkVknzt8hpB7tszh+vnWSLK/D6gTzNMRMxvp7 t3/hlFY7rMCGsRd8kIlsNplg7VJlsDZYOTCMTDVm8vHQxkNr7bXDoqQzSpdwpcjcTogV Y89TSItwEsibXHviFoxg9OIbJnyRkLx4J6/TEg6qf2bA75oPcWvFLdtAChB8Pf3VgcHn 8CxILn5Dd14YgaVaudoX73OY+EPqjgl4zIEqSeJUnAjOuZ++H1OzLkOu+kKmeUSmtPqM s+XQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=fucDkV3i; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id w10-20020aa7dcca000000b0051a21104f02si1036534edu.346.2023.07.26.06.49.55; Wed, 26 Jul 2023 06:50:20 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=fucDkV3i; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233259AbjGZNBB (ORCPT + 99 others); Wed, 26 Jul 2023 09:01:01 -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: 8bit X-Spam-Status: No, score=-7.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_HI, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net 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: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./function_profile_enabled" ls-87 [000] ...1. 8067.616108: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./trace_stat" ls-87 [000] ...1. 8067.616115: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./set_graph_notrace" ls-87 [000] ...1. 8067.616122: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./set_graph_function" ls-87 [000] ...1. 8067.616129: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./set_ftrace_notrace" ls-87 [000] ...1. 8067.616135: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./set_ftrace_filter" ls-87 [000] ...1. 8067.616143: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./touched_functions" ls-87 [000] ...1. 8067.616237: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./enabled_functions" ls-87 [000] ...1. 8067.616245: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./available_filter_functions" ls-87 [000] ...1. 8067.616253: getname_flags__exit: (vfs_fstatat+0x3c/0x70 <- getname_flags) arg1="./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 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 btf *btf, return NULL; } -static const struct btf_param *find_btf_func_param(const char *funcname, s32 *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; - if (!funcname || !nr) - return ERR_PTR(-EINVAL); + if (ctx->btf) + return 0; - t = btf_find_func_proto(funcname, &btf); - if (!t) - return (const struct btf_param *)t; + if (!ctx->funcname) + return -EINVAL; - param = btf_get_func_param(t, nr); - if (IS_ERR_OR_NULL(param)) - goto err; + type = btf_find_func_proto(ctx->funcname, &btf); + if (!type) + return -ENOENT; - /* Hide the first 'data' argument of tracepoint */ - if (tracepoint) { - (*nr)--; - param++; + ctx->btf = btf; + ctx->proto = type; + + /* ctx->params is optional, since func(void) will not have params. */ + nr = 0; + param = 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++; + } } - if (*nr > 0) { - *btf_p = btf; - return param; + if (nr > 0) { + ctx->nr_params = nr; + ctx->params = param; + } else { + ctx->nr_params = 0; + ctx->params = NULL; } -err: - btf_put(btf); - return NULL; + return 0; } static void clear_btf_context(struct traceprobe_parse_context *ctx) @@ -402,6 +410,7 @@ static void clear_btf_context(struct traceprobe_parse_context *ctx) if (ctx->btf) { btf_put(ctx->btf); ctx->btf = NULL; + ctx->proto = NULL; ctx->params = NULL; ctx->nr_params = 0; } @@ -517,7 +526,7 @@ static int parse_btf_arg(char *varname, const struct btf_param *params; const struct btf_type *type; char *field = NULL; - int i, is_ptr; + int i, is_ptr, ret; u32 tid; if (WARN_ON_ONCE(!ctx->funcname)) @@ -533,17 +542,32 @@ static int parse_btf_arg(char *varname, return -EOPNOTSUPP; } - if (!ctx->params) { - params = 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") != 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) == 0) { + if (ctx->proto->type == 0) { + trace_probe_log_err(ctx->offset, NO_RETVAL); + return -ENOENT; + } + tid = ctx->proto->type; + } else + tid = 0; + code->op = FETCH_OP_RETVAL; + goto found; + } + + if (!ctx->btf) { + ret = query_btf_context(ctx); + if (ret < 0 || ctx->nr_params == 0) { trace_probe_log_err(ctx->offset, NO_BTF_ENTRY); return PTR_ERR(params); } - ctx->params = params; - } else - params = ctx->params; + } + params = ctx->params; for (i = 0; i < ctx->nr_params; i++) { const char *name = btf_name_by_offset(ctx->btf, params[i].name_off); @@ -554,7 +578,6 @@ static int parse_btf_arg(char *varname, code->param = i + 1; else code->param = i; - tid = params[i].type; goto found; } @@ -579,7 +602,7 @@ static int parse_btf_arg(char *varname, return 0; } -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 = ctx->btf; @@ -591,27 +614,6 @@ static const struct fetch_type *parse_btf_arg_type( return find_fetch_type(typestr, ctx->flags); } -static const struct fetch_type *parse_btf_retval_type( - struct traceprobe_parse_context *ctx) -{ - const char *typestr = 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 = btf_find_func_proto(ctx->funcname, &btf); - if (type) { - type = btf_type_skip_modifiers(btf, type->type, NULL); - if (!IS_ERR_OR_NULL(type)) - typestr = 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 **pcode, return 0; } -static bool is_btf_retval_void(const char *funcname) -{ - const struct btf_type *t; - struct btf *btf; - bool ret; - - t = btf_find_func_proto(funcname, &btf); - if (!t) - return false; - - ret = (t->type == 0); - btf_put(btf); - return ret; -} #else static void clear_btf_context(struct traceprobe_parse_context *ctx) { ctx->btf = NULL; } -static const struct btf_param *find_btf_func_param(const char *funcname, s32 *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 **pcode, return -EOPNOTSUPP; } -#define parse_btf_arg_type(ctx) \ +#define find_fetch_type_from_btf_type(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)) -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 = *pcode; int err = TP_ERR_BAD_VAR; + char *arg = orig_arg + 1; + unsigned long param; int ret = 0; int len; @@ -711,18 +697,17 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, goto inval; } - 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; - } + if (str_has_prefix(arg, "retval")) { + if (!(ctx->flags & TPARG_FL_RETURN)) { + err = TP_ERR_RETVAL_ON_PROBE; + goto inval; + } + if (!(ctx->flags & TPARG_FL_KERNEL) || + !IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS)) { code->op = FETCH_OP_RETVAL; return 0; } - err = TP_ERR_RETVAL_ON_PROBE; - goto inval; + return parse_btf_arg(orig_arg, pcode, end, ctx); } len = str_has_prefix(arg, "stack"); @@ -824,7 +809,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, switch (arg[0]) { case '$': - ret = parse_probe_vars(arg + 1, type, code, ctx); + ret = parse_probe_vars(arg, type, pcode, end, ctx); break; case '%': /* named register */ @@ -1121,12 +1106,9 @@ 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) { - if (ctx->last_type) - parg->type = parse_btf_arg_type(ctx); - else if (ctx->flags & TPARG_FL_RETURN) - parg->type = parse_btf_retval_type(ctx); - } + if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && + !t && ctx->last_type) + parg->type = find_fetch_type_from_btf_type(ctx); ret = -EINVAL; /* Store operation */ @@ -1415,7 +1397,6 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], const struct btf_param *params = NULL; int i, j, n, used, ret, args_idx = -1; const char **new_argv = NULL; - int nr_params; ret = argv_has_var_arg(argc, argv, &args_idx, ctx); if (ret < 0) @@ -1426,9 +1407,8 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], return NULL; } - params = find_btf_func_param(ctx->funcname, &nr_params, &ctx->btf, - ctx->flags & TPARG_FL_TPOINT); - if (IS_ERR_OR_NULL(params)) { + ret = query_btf_context(ctx); + if (ret < 0 || ctx->nr_params == 0) { if (args_idx != -1) { /* $arg* requires BTF info */ trace_probe_log_err(0, NOSUP_BTFARG); @@ -1437,8 +1417,6 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], *new_argc = argc; return NULL; } - ctx->params = params; - ctx->nr_params = nr_params; if (args_idx >= 0) *new_argc = argc + ctx->nr_params - 1; @@ -1453,7 +1431,7 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], for (i = 0, j = 0; i < argc; i++) { trace_probe_log_set_index(i + 2); if (i == args_idx) { - for (n = 0; n < nr_params; n++) { + for (n = 0; n < ctx->nr_params; n++) { ret = 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 */