From patchwork Mon Aug 7 02:55:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Masami Hiramatsu (Google)" X-Patchwork-Id: 131647 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:c44e:0:b0:3f2:4152:657d with SMTP id w14csp1197261vqr; Sun, 6 Aug 2023 20:07:05 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHmW6AYhiFzVkvLaZjmRlqTkGF8osHYNmXVZXTAA2ADB6F+qyTVY9WksKllDt830tQ93fbK X-Received: by 2002:a19:921a:0:b0:4f8:661f:60a4 with SMTP id u26-20020a19921a000000b004f8661f60a4mr3701939lfd.41.1691377624779; Sun, 06 Aug 2023 20:07:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1691377624; cv=none; d=google.com; s=arc-20160816; b=QoGaZqmvknllqFszxD52I2olISvTXEqNATpA8CBkdG1En7IeHIgGUNDiFk5r3hYC1C H+51CA/XU+eOIFaJBlWSvOjyt30UxYi7hK26QXh9FF675JzQgYNnS1+/Xrjq8PU8bBuO Bi2ynyvfnq1avnbAiXq6reEYS4XzX5Uv9LxsMJyP4cDES5XTLknUBxgH6M2ICRGdqe05 MeXrhM/Q9CVpfQIuPUGQtNLNRHZyLyd+O2Ob5SDbjZ6l2WNcsG63UQfYfQnxoQWnob15 VGuFWaii82gJ9Pj5DukQQ5Fs7ffwod1VfFSK76xwz2ZpX++YSPo+ihLMyAq3c5kFHvNY Rapg== 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=bAqewmvnA0kirL8YrVgP2I3YKFPUK88ztwqbmIMBV6g=; fh=raxTTJvC+dSJYbgfX5JG5gvrVjxTZXczN0HF8iDayLY=; b=XkFNa3X/3OOKvjGfUnuYkMSIHyqurLcYlV8huvviEWh+WM9AAYEAmoKOo1jHrMyK2e ebcghKsrSwEWJ2KnqzxuPZZ6knMRsqwyMsh/+3TTyJfgF9CZE98Ge+Aet6XobPKIJHWb Pd+oYzai3K+rb99esXEqXBf20eqVZXhMuOvV4fQVAtWwQxQqTszSoAk0e9Pf8e0pCWdr POd1a0+WGFCjDZjljaEUtooxaiVtqk//4VImAQ6AIyRiobJAmk7zeiy99Kco2UQQdb0m mSwYOz2Wp8n6USC6bF0gaSV5dNIA9feZiDSntlDa+5L/JVKT9J4AgniMgeWDguYx3zws OPnw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=TlD9iIUB; 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: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id c9-20020a170906d18900b0099b41fa79f3si4944228ejz.331.2023.08.06.20.06.40; Sun, 06 Aug 2023 20:07:04 -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=TlD9iIUB; 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 S229941AbjHGCzt (ORCPT + 99 others); Sun, 6 Aug 2023 22:55:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48436 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229554AbjHGCzr (ORCPT ); Sun, 6 Aug 2023 22:55:47 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5E2FA1732; Sun, 6 Aug 2023 19:55:14 -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 D4DFB61147; Mon, 7 Aug 2023 02:55:13 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 511F6C433C8; Mon, 7 Aug 2023 02:55:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1691376913; bh=eF7puk083UBF9yxp0SUFHWsJdaAhex/RA3xisdjFIEA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TlD9iIUByAoroIC8Fz7i7EuEE4iCNVPbFxX0iGnElfVItABlMqe3qu1siXdO2/vM8 U56fdwnsx4Bewe5yQQdns6d3H/R13WtWNGDitximJdwxEgeBBdM0Lp1W0VPZY/Vjon EpcdPFtmbqCmlS2BL+9sh46UJL/1bjO4mwAQpKInTNTDS1/2WhJAy9qfC9vQh18XvI oxezn41SNe7dMByK0ijjNP2SCi3VuCNFMF2/gf5ZVsVNA+XBVB7wc6mC6wUpWi8A1I 9C5yin8LQh6G1aDRTjf4n4y70/+8Bo/F5oAClvS1VnMvvNL1S4uT1dqkf42ZQeLV2q bPeRo4XIY1KDQ== 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 4/9] tracing/probes: Support BTF based data structure field access Date: Mon, 7 Aug 2023 11:55:08 +0900 Message-Id: <169137690862.271367.15345269547801333600.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 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS 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 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1773537984572027169 X-GMAIL-MSGID: 1773537984572027169 From: Masami Hiramatsu (Google) Using BTF to access the fields of a data structure. You can use this for accessing the field with '->' or '.' operation with BTF argument. # echo 't sched_switch next=next->pid vruntime=next->se.vruntime' \ > dynamic_events # echo 1 > events/tracepoints/sched_switch/enable # head -n 40 trace | tail -0 [000] d..3. 272.565382: sched_switch: (__probestub_sched_switch+0x4/0x10) next=26 vruntime=956533179 kcompactd0-26 [000] d..3. 272.565406: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 -0 [000] d..3. 273.069441: sched_switch: (__probestub_sched_switch+0x4/0x10) next=9 vruntime=956533179 kworker/0:1-9 [000] d..3. 273.069464: sched_switch: (__probestub_sched_switch+0x4/0x10) next=26 vruntime=956579181 kcompactd0-26 [000] d..3. 273.069480: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 -0 [000] d..3. 273.141434: sched_switch: (__probestub_sched_switch+0x4/0x10) next=22 vruntime=956533179 kworker/u2:1-22 [000] d..3. 273.141461: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 -0 [000] d..3. 273.480872: sched_switch: (__probestub_sched_switch+0x4/0x10) next=22 vruntime=956585857 kworker/u2:1-22 [000] d..3. 273.480905: sched_switch: (__probestub_sched_switch+0x4/0x10) next=70 vruntime=959533179 sh-70 [000] d..3. 273.481102: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 Signed-off-by: Masami Hiramatsu (Google) Reviewed-by: Alan Maguire --- Changes in v2: - Use new BTF API for finding the member. Changes in v3: - Update according to previous changes in the series. Changes in v5: - Fix offset for anonymous union/structure members. - Add dot and allow operators in README. --- kernel/trace/trace.c | 3 - kernel/trace/trace_probe.c | 230 +++++++++++++++++++++++++++++++++++++++----- kernel/trace/trace_probe.h | 11 ++ 3 files changed, 216 insertions(+), 28 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index be847d45d81c..5ed90855a3e3 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5716,7 +5716,8 @@ static const char readme_msg[] = "\t fetcharg: (%|$), @
, @[+|-],\n" #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS - "\t $stack, $stack, $retval, $comm, $arg, \n" + "\t $stack, $stack, $retval, $comm, $arg,\n" + "\t [->field[->field|.field...]],\n" #else "\t $stack, $stack, $retval, $comm, $arg,\n" #endif diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index c3ac5698e80b..821f43e5c52b 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -310,16 +310,14 @@ static u32 btf_type_int(const struct btf_type *t) return *(u32 *)(t + 1); } -static const char *type_from_btf_id(struct btf *btf, s32 id) +static const char *fetch_type_from_btf_type(struct btf *btf, + const struct btf_type *type, + struct traceprobe_parse_context *ctx) { - const struct btf_type *t; u32 intdata; - s32 tid; /* TODO: const char * could be converted as a string */ - t = btf_type_skip_modifiers(btf, id, &tid); - - switch (BTF_INFO_KIND(t->info)) { + switch (BTF_INFO_KIND(type->info)) { case BTF_KIND_ENUM: /* enum is "int", so convert to "s32" */ return "s32"; @@ -332,7 +330,7 @@ static const char *type_from_btf_id(struct btf *btf, s32 id) else return "x32"; case BTF_KIND_INT: - intdata = btf_type_int(t); + intdata = btf_type_int(type); if (BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) { switch (BTF_INT_BITS(intdata)) { case 8: @@ -355,6 +353,10 @@ static const char *type_from_btf_id(struct btf *btf, s32 id) case 64: return "u64"; } + /* bitfield, size is encoded in the type */ + ctx->last_bitsize = BTF_INT_BITS(intdata); + ctx->last_bitoffs += BTF_INT_OFFSET(intdata); + return "u64"; } } /* TODO: support other types */ @@ -406,15 +408,136 @@ static void clear_btf_context(struct traceprobe_parse_context *ctx) } } -static int parse_btf_arg(const char *varname, struct fetch_insn *code, +/* Return 1 if the field separater is arrow operator ('->') */ +static int split_next_field(char *varname, char **next_field, + struct traceprobe_parse_context *ctx) +{ + char *field; + int ret = 0; + + field = strpbrk(varname, ".-"); + if (field) { + if (field[0] == '-' && field[1] == '>') { + field[0] = '\0'; + field += 2; + ret = 1; + } else if (field[0] == '.') { + field[0] = '\0'; + field += 1; + } else { + trace_probe_log_err(ctx->offset + field - varname, BAD_HYPHEN); + return -EINVAL; + } + *next_field = field; + } + + return ret; +} + +/* + * Parse the field of data structure. The @type must be a pointer type + * pointing the target data structure type. + */ +static int parse_btf_field(char *fieldname, const struct btf_type *type, + struct fetch_insn **pcode, struct fetch_insn *end, + struct traceprobe_parse_context *ctx) +{ + struct fetch_insn *code = *pcode; + const struct btf_member *field; + u32 bitoffs, anon_offs; + char *next; + int is_ptr; + s32 tid; + + do { + /* Outer loop for solving arrow operator ('->') */ + if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) { + trace_probe_log_err(ctx->offset, NO_PTR_STRCT); + return -EINVAL; + } + /* Convert a struct pointer type to a struct type */ + type = btf_type_skip_modifiers(ctx->btf, type->type, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } + + bitoffs = 0; + do { + /* Inner loop for solving dot operator ('.') */ + next = NULL; + is_ptr = split_next_field(fieldname, &next, ctx); + if (is_ptr < 0) + return is_ptr; + + anon_offs = 0; + field = btf_find_struct_member(ctx->btf, type, fieldname, + &anon_offs); + if (!field) { + trace_probe_log_err(ctx->offset, NO_BTF_FIELD); + return -ENOENT; + } + /* Add anonymous structure/union offset */ + bitoffs += anon_offs; + + /* Accumulate the bit-offsets of the dot-connected fields */ + if (btf_type_kflag(type)) { + bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset); + ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset); + } else { + bitoffs += field->offset; + ctx->last_bitsize = 0; + } + + type = btf_type_skip_modifiers(ctx->btf, field->type, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } + + ctx->offset += next - fieldname; + fieldname = next; + } while (!is_ptr && fieldname); + + if (++code == end) { + trace_probe_log_err(ctx->offset, TOO_MANY_OPS); + return -EINVAL; + } + code->op = FETCH_OP_DEREF; /* TODO: user deref support */ + code->offset = bitoffs / 8; + *pcode = code; + + ctx->last_bitoffs = bitoffs % 8; + ctx->last_type = type; + } while (fieldname); + + return 0; +} + +static int parse_btf_arg(char *varname, + struct fetch_insn **pcode, struct fetch_insn *end, struct traceprobe_parse_context *ctx) { + struct fetch_insn *code = *pcode; const struct btf_param *params; - int i; + const struct btf_type *type; + char *field = NULL; + int i, is_ptr; + u32 tid; if (WARN_ON_ONCE(!ctx->funcname)) return -EINVAL; + is_ptr = split_next_field(varname, &field, ctx); + if (is_ptr < 0) + return is_ptr; + if (!is_ptr && field) { + /* dot-connected field on an argument is not supported. */ + trace_probe_log_err(ctx->offset + field - varname, + NOSUP_DAT_ARG); + return -EOPNOTSUPP; + } + if (!ctx->params) { params = find_btf_func_param(ctx->funcname, &ctx->nr_params, &ctx->btf, @@ -436,24 +559,39 @@ static int parse_btf_arg(const char *varname, struct fetch_insn *code, code->param = i + 1; else code->param = i; - return 0; + + tid = params[i].type; + goto found; } } trace_probe_log_err(ctx->offset, NO_BTFARG); return -ENOENT; + +found: + type = btf_type_skip_modifiers(ctx->btf, tid, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } + /* Initialize the last type information */ + ctx->last_type = type; + ctx->last_bitoffs = 0; + ctx->last_bitsize = 0; + if (field) { + ctx->offset += field - varname; + return parse_btf_field(field, type, pcode, end, ctx); + } + return 0; } -static const struct fetch_type *parse_btf_arg_type(int arg_idx, +static const struct fetch_type *parse_btf_arg_type( struct traceprobe_parse_context *ctx) { struct btf *btf = ctx->btf; const char *typestr = NULL; - if (btf && ctx->params) { - if (ctx->flags & TPARG_FL_TPOINT) - arg_idx--; - typestr = type_from_btf_id(btf, ctx->params[arg_idx].type); - } + if (btf && ctx->last_type) + typestr = fetch_type_from_btf_type(btf, ctx->last_type, ctx); return find_fetch_type(typestr, ctx->flags); } @@ -462,14 +600,16 @@ static const struct fetch_type *parse_btf_retval_type( struct traceprobe_parse_context *ctx) { const char *typestr = NULL; - const struct btf_type *t; + const struct btf_type *type; struct btf *btf; if (ctx->funcname) { /* Do not use ctx->btf, because it must be used with ctx->param */ - t = btf_find_func_proto(ctx->funcname, &btf); - if (t) { - typestr = type_from_btf_id(btf, t->type); + 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); } } @@ -477,6 +617,28 @@ static const struct fetch_type *parse_btf_retval_type( return find_fetch_type(typestr, ctx->flags); } +static int parse_btf_bitfield(struct fetch_insn **pcode, + struct traceprobe_parse_context *ctx) +{ + struct fetch_insn *code = *pcode; + + if ((ctx->last_bitsize % 8 == 0) && ctx->last_bitoffs == 0) + return 0; + + code++; + if (code->op != FETCH_OP_NOP) { + trace_probe_log_err(ctx->offset, TOO_MANY_OPS); + return -EINVAL; + } + *pcode = code; + + code->op = FETCH_OP_MOD_BF; + code->lshift = 64 - (ctx->last_bitsize + ctx->last_bitoffs); + code->rshift = 64 - ctx->last_bitsize; + code->basesize = 64 / 8; + return 0; +} + static bool is_btf_retval_void(const char *funcname) { const struct btf_type *t; @@ -503,14 +665,22 @@ static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr return ERR_PTR(-EOPNOTSUPP); } -static int parse_btf_arg(const char *varname, struct fetch_insn *code, +static int parse_btf_arg(char *varname, + struct fetch_insn **pcode, struct fetch_insn *end, struct traceprobe_parse_context *ctx) { trace_probe_log_err(ctx->offset, NOSUP_BTFARG); return -EOPNOTSUPP; } -#define parse_btf_arg_type(idx, ctx) \ +static int parse_btf_bitfield(struct fetch_insn **pcode, + struct traceprobe_parse_context *ctx) +{ + trace_probe_log_err(ctx->offset, NOSUP_BTFARG); + return -EOPNOTSUPP; +} + +#define parse_btf_arg_type(ctx) \ find_fetch_type(NULL, ctx->flags) #define parse_btf_retval_type(ctx) \ @@ -778,6 +948,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = deref; code->offset = offset; + /* Reset the last type if used */ + ctx->last_type = NULL; } break; case '\\': /* Immediate value */ @@ -801,7 +973,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, trace_probe_log_err(ctx->offset, NOSUP_BTFARG); return -EINVAL; } - ret = parse_btf_arg(arg, code, ctx); + ret = parse_btf_arg(arg, pcode, end, ctx); break; } } @@ -947,6 +1119,7 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, goto out; code[FETCH_INSN_MAX - 1].op = FETCH_OP_END; + ctx->last_type = NULL; ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1], ctx); if (ret) @@ -954,9 +1127,9 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, /* Update storing type if BTF is available */ 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) + 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); } @@ -1031,6 +1204,11 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, trace_probe_log_err(ctx->offset + t - arg, BAD_BITFIELD); goto fail; } + } else if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && + ctx->last_type) { + ret = parse_btf_bitfield(&code, ctx); + if (ret) + goto fail; } ret = -EINVAL; /* Loop(Array) operation */ diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 4dc91460a75d..6111f1ffca6c 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -388,6 +388,9 @@ struct traceprobe_parse_context { 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 */ + const struct btf_type *last_type; /* Saved type */ + u32 last_bitoffs; /* Saved bitoffs */ + u32 last_bitsize; /* Saved bitsize */ unsigned int flags; int offset; }; @@ -503,7 +506,13 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."),\ C(NOFENTRY_ARGS, "$arg* can be used only on function entry"), \ C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \ - C(ARGS_2LONG, "$arg* failed because the argument list is too long"), + C(ARGS_2LONG, "$arg* failed because the argument list is too long"), \ + C(ARGIDX_2BIG, "$argN index is too big"), \ + C(NO_PTR_STRCT, "This is not a pointer to union/structure."), \ + C(NOSUP_DAT_ARG, "Non pointer structure/union argument is not supported."),\ + C(BAD_HYPHEN, "Failed to parse single hyphen. Forgot '>'?"), \ + C(NO_BTF_FIELD, "This field is not found."), \ + C(BAD_BTF_TID, "Failed to get BTF type info."), #undef C #define C(a, b) TP_ERR_##a