From nobody Fri Oct 10 23:16:18 2025 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E9FF12D543E for ; Thu, 12 Jun 2025 19:49:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749757789; cv=none; b=Z+2WU6LA6eIaOvCrs5/kZvgczQ9JejlxENY+n6Rcig45LMdo+ipTCAGvrVgcH7OgvzFdmnJlI6B4lDnPIOOj6myGTFRinczUGHG+QMMvxfj4w3VRctfJxPH8soPyYEUtsHVXnzOrhhWO/Ncra/fQpdCEHe9QkapGgWkjYUq8uN8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749757789; c=relaxed/simple; bh=sL0QzF3uQAud4FchfdGpAc1VAF/3gSxQsFo0+LfNj0Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=j0a87WjAiwjc18ieL79ryFE1WfhixuXrvb4g9N7raTTZGnAYL9wF6DvBZLGexqtxVa6wPqVgbrArM+4wMMvlqEqksBvfqpQUCjsNisjV+LZ/MYQKiqD+9i04PyrCnQ8PMXN6Y86AXmRW1lYnzMOkTWvHUmcdTiDDPWIPATF+etk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--blakejones.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=sG+fa8Pl; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--blakejones.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="sG+fa8Pl" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-3138e64b3f1so1481699a91.3 for ; Thu, 12 Jun 2025 12:49:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1749757786; x=1750362586; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=v3sg1BJGPzEyje7VYNfhPoJYsL8//xYzfwrTk24e9bY=; b=sG+fa8Pl1phImEbTqsWlMc4p22i82Fr3fTPtL1yp/8+XbXB5WbQslOScJUCYCDlrVy k1xKuat59v9gwywtm4ak0NP0/1TYx5u/54aRAQcWI0Y1XjfBOS43nW0OkKr6zNKA7LPw 2Ae3M5pPke+0XODBrdXh9Zpb3rszEzelweEHkkfRGh5WzT3N77jdfkUFgqReqLKQQ2/5 84moLJTfaQXFEtJ5I9H3+f+Yh7/UZ7JY5P4BEa32fz2J8JGDs4GpHXl+BeLCrt9B6o0+ FVlzZ9XHlDiXbzjDndXKSZ2zb+l74nvGBVzv+HFjPAKMY5zjwovsFkpPtzxclAsOLPaN aqPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749757786; x=1750362586; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=v3sg1BJGPzEyje7VYNfhPoJYsL8//xYzfwrTk24e9bY=; b=T6oek9pjyiyJiwDaNmMgbUQifZHwdWEraAvD1yqWpZTrQZBj2QWY8QTGQ6ROAzVylI 3ewQqvQ1wn7yq5wS9xiAPVioga2cCOtFe6kUphc57hQFZpqFDnjdzel9j6scQ1t39Vk6 6PY4EfI3VXiYJ4OAFayB1XkmZX+8EBmJ7LotFkKygvL5jTAz2QqxzmLh22TKBb+YCszv DSrDq5CO16kOjJKx93n9Y/3e7fZm9d97vZXi/erd02667KM2CZnPqIMaal50yF0aV6pS kqI5nlDqOz+lrBc973o1vddAnHE69C2X7clsoDXLJa2jyz1oYcYj4U2voSDNZaWtruT4 nelg== X-Forwarded-Encrypted: i=1; AJvYcCVPWAbwNksVD/naXE4nGGHmJG3P4U/LhDhVy37Y75MYtfXdXFe0qB5BElLxYc2kuwMT0tJBkHN7A5XcFao=@vger.kernel.org X-Gm-Message-State: AOJu0Yz38Pjol+H+H6NLyYg131pgICKWmob9xUeuE5mTWBX9Nh3dbgXP oOiidhT1BRmkSP0eCka49Vb6N+48mCkNmq+RMjqQI/3B7BDvDvd+uiPgG9k19QHKxHuwQJkjsmZ 1+uqVAj0vrYaE8ANGVdepxg== X-Google-Smtp-Source: AGHT+IFeGIi9ZOJZcfn/cJag5N0/xntrLUcO1I0ivEyMLAmYINbjbADQn3mDYhB3lHRLSvDGbBEjW1RYTi56KsmS X-Received: from pjtd9.prod.google.com ([2002:a17:90b:49:b0:313:1c10:3595]) (user=blakejones job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:1c12:b0:312:db8:dbd1 with SMTP id 98e67ed59e1d1-313d9c3a0bemr527852a91.5.1749757786375; Thu, 12 Jun 2025 12:49:46 -0700 (PDT) Date: Thu, 12 Jun 2025 12:49:36 -0700 In-Reply-To: <20250612194939.162730-1-blakejones@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250612194939.162730-1-blakejones@google.com> X-Mailer: git-send-email 2.50.0.rc1.591.g9c95f17f64-goog Message-ID: <20250612194939.162730-3-blakejones@google.com> Subject: [PATCH v4 2/5] perf: collect BPF metadata from existing BPF programs From: Blake Jones To: Arnaldo Carvalho de Melo , Namhyung Kim , Ian Rogers , Jiri Olsa , Peter Zijlstra , Ingo Molnar Cc: Mark Rutland , Alexander Shishkin , Adrian Hunter , Kan Liang , Steven Rostedt , Tomas Glozar , James Clark , Leo Yan , Guilherme Amadio , Yang Jihong , Charlie Jenkins , Chun-Tse Shao , Aditya Gupta , Athira Rajeev , Zhongqiu Han , Andi Kleen , Dmitry Vyukov , Yujie Liu , Graham Woodward , Yicong Yang , Ben Gainey , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, bpf@vger.kernel.org, Blake Jones Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Look for .rodata maps, find ones with 'bpf_metadata_' variables, extract their values as strings, and create a new PERF_RECORD_BPF_METADATA synthetic event using that data. The code gets invoked from the existing routine perf_event__synthesize_one_bpf_prog(). For example, a BPF program with the following variables: const char bpf_metadata_version[] SEC(".rodata") =3D "3.14159"; int bpf_metadata_value[] SEC(".rodata") =3D 42; would generate a PERF_RECORD_BPF_METADATA record with: .prog_name =3D .nr_entries =3D 2 .entries[0].key =3D "version" .entries[0].value =3D "3.14159" .entries[1].key =3D "value" .entries[1].value =3D "42" Each of the BPF programs and subprograms that share those variables would get a distinct PERF_RECORD_BPF_METADATA record, with the ".prog_name" showing the name of each program or subprogram. The prog_name is deliberately the same as the ".name" field in the corresponding PERF_RECORD_KSYMBOL record. This code only gets invoked if support for displaying BTF char arrays as strings is detected. Signed-off-by: Blake Jones --- tools/lib/perf/include/perf/event.h | 18 ++ tools/perf/util/bpf-event.c | 332 ++++++++++++++++++++++++++++ tools/perf/util/bpf-event.h | 12 + 3 files changed, 362 insertions(+) diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/p= erf/event.h index 09b7c643ddac..6608f1e3701b 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -467,6 +467,22 @@ struct perf_record_compressed2 { char data[]; }; =20 +#define BPF_METADATA_KEY_LEN 64 +#define BPF_METADATA_VALUE_LEN 256 +#define BPF_PROG_NAME_LEN KSYM_NAME_LEN + +struct perf_record_bpf_metadata_entry { + char key[BPF_METADATA_KEY_LEN]; + char value[BPF_METADATA_VALUE_LEN]; +}; + +struct perf_record_bpf_metadata { + struct perf_event_header header; + char prog_name[BPF_PROG_NAME_LEN]; + __u64 nr_entries; + struct perf_record_bpf_metadata_entry entries[]; +}; + enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_USER_TYPE_START =3D 64, PERF_RECORD_HEADER_ATTR =3D 64, @@ -489,6 +505,7 @@ enum perf_user_event_type { /* above any possible kerne= l type */ PERF_RECORD_COMPRESSED =3D 81, PERF_RECORD_FINISHED_INIT =3D 82, PERF_RECORD_COMPRESSED2 =3D 83, + PERF_RECORD_BPF_METADATA =3D 84, PERF_RECORD_HEADER_MAX }; =20 @@ -530,6 +547,7 @@ union perf_event { struct perf_record_header_feature feat; struct perf_record_compressed pack; struct perf_record_compressed2 pack2; + struct perf_record_bpf_metadata bpf_metadata; }; =20 #endif /* __LIBPERF_EVENT_H */ diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index c81444059ad0..1f6e76ee6024 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -1,13 +1,21 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include +#include +#include #include +#include #include #include #include +#include #include #include +#include #include +#include #include +#include #include #include "bpf-event.h" #include "bpf-utils.h" @@ -151,6 +159,319 @@ static int synthesize_bpf_prog_name(char *buf, int si= ze, return name_len; } =20 +#ifdef HAVE_LIBBPF_STRINGS_SUPPORT + +#define BPF_METADATA_PREFIX "bpf_metadata_" +#define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1) + +static bool name_has_bpf_metadata_prefix(const char **s) +{ + if (strncmp(*s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) !=3D 0) + return false; + *s +=3D BPF_METADATA_PREFIX_LEN; + return true; +} + +struct bpf_metadata_map { + struct btf *btf; + const struct btf_type *datasec; + void *rodata; + size_t rodata_size; + unsigned int num_vars; +}; + +static int bpf_metadata_read_map_data(__u32 map_id, struct bpf_metadata_ma= p *map) +{ + int map_fd; + struct bpf_map_info map_info; + __u32 map_info_len; + int key; + struct btf *btf; + const struct btf_type *datasec; + struct btf_var_secinfo *vsi; + unsigned int vlen, vars; + void *rodata; + + map_fd =3D bpf_map_get_fd_by_id(map_id); + if (map_fd < 0) + return -1; + + memset(&map_info, 0, sizeof(map_info)); + map_info_len =3D sizeof(map_info); + if (bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len) < 0) + goto out_close; + + /* If it's not an .rodata map, don't bother. */ + if (map_info.type !=3D BPF_MAP_TYPE_ARRAY || + map_info.key_size !=3D sizeof(int) || + map_info.max_entries !=3D 1 || + !map_info.btf_value_type_id || + !strstr(map_info.name, ".rodata")) { + goto out_close; + } + + btf =3D btf__load_from_kernel_by_id(map_info.btf_id); + if (!btf) + goto out_close; + datasec =3D btf__type_by_id(btf, map_info.btf_value_type_id); + if (!btf_is_datasec(datasec)) + goto out_free_btf; + + /* + * If there aren't any variables with the "bpf_metadata_" prefix, + * don't bother. + */ + vlen =3D btf_vlen(datasec); + vsi =3D btf_var_secinfos(datasec); + vars =3D 0; + for (unsigned int i =3D 0; i < vlen; i++, vsi++) { + const struct btf_type *t_var =3D btf__type_by_id(btf, vsi->type); + const char *name =3D btf__name_by_offset(btf, t_var->name_off); + + if (name_has_bpf_metadata_prefix(&name)) + vars++; + } + if (vars =3D=3D 0) + goto out_free_btf; + + rodata =3D zalloc(map_info.value_size); + if (!rodata) + goto out_free_btf; + key =3D 0; + if (bpf_map_lookup_elem(map_fd, &key, rodata)) { + free(rodata); + goto out_free_btf; + } + close(map_fd); + + map->btf =3D btf; + map->datasec =3D datasec; + map->rodata =3D rodata; + map->rodata_size =3D map_info.value_size; + map->num_vars =3D vars; + return 0; + +out_free_btf: + btf__free(btf); +out_close: + close(map_fd); + return -1; +} + +struct format_btf_ctx { + char *buf; + size_t buf_size; + size_t buf_idx; +}; + +static void format_btf_cb(void *arg, const char *fmt, va_list ap) +{ + int n; + struct format_btf_ctx *ctx =3D (struct format_btf_ctx *)arg; + + n =3D vsnprintf(ctx->buf + ctx->buf_idx, ctx->buf_size - ctx->buf_idx, + fmt, ap); + ctx->buf_idx +=3D n; + if (ctx->buf_idx >=3D ctx->buf_size) + ctx->buf_idx =3D ctx->buf_size; +} + +static void format_btf_variable(struct btf *btf, char *buf, size_t buf_siz= e, + const struct btf_type *t, const void *btf_data) +{ + struct format_btf_ctx ctx =3D { + .buf =3D buf, + .buf_idx =3D 0, + .buf_size =3D buf_size, + }; + const struct btf_dump_type_data_opts opts =3D { + .sz =3D sizeof(struct btf_dump_type_data_opts), + .skip_names =3D 1, + .compact =3D 1, + .emit_strings =3D 1, + }; + struct btf_dump *d; + size_t btf_size; + + d =3D btf_dump__new(btf, format_btf_cb, &ctx, NULL); + btf_size =3D btf__resolve_size(btf, t->type); + btf_dump__dump_type_data(d, t->type, btf_data, btf_size, &opts); + btf_dump__free(d); +} + +static void bpf_metadata_fill_event(struct bpf_metadata_map *map, + struct perf_record_bpf_metadata *bpf_metadata_event) +{ + struct btf_var_secinfo *vsi; + unsigned int i, vlen; + + memset(bpf_metadata_event->prog_name, 0, BPF_PROG_NAME_LEN); + vlen =3D btf_vlen(map->datasec); + vsi =3D btf_var_secinfos(map->datasec); + + for (i =3D 0; i < vlen; i++, vsi++) { + const struct btf_type *t_var =3D btf__type_by_id(map->btf, + vsi->type); + const char *name =3D btf__name_by_offset(map->btf, + t_var->name_off); + const __u64 nr_entries =3D bpf_metadata_event->nr_entries; + struct perf_record_bpf_metadata_entry *entry; + + if (!name_has_bpf_metadata_prefix(&name)) + continue; + + if (nr_entries >=3D (__u64)map->num_vars) + break; + + entry =3D &bpf_metadata_event->entries[nr_entries]; + memset(entry, 0, sizeof(*entry)); + snprintf(entry->key, BPF_METADATA_KEY_LEN, "%s", name); + format_btf_variable(map->btf, entry->value, + BPF_METADATA_VALUE_LEN, t_var, + map->rodata + vsi->offset); + bpf_metadata_event->nr_entries++; + } +} + +static void bpf_metadata_free_map_data(struct bpf_metadata_map *map) +{ + btf__free(map->btf); + free(map->rodata); +} + +static struct bpf_metadata *bpf_metadata_alloc(__u32 nr_prog_tags, + __u32 nr_variables) +{ + struct bpf_metadata *metadata; + size_t event_size; + + metadata =3D zalloc(sizeof(struct bpf_metadata)); + if (!metadata) + return NULL; + + metadata->prog_names =3D zalloc(nr_prog_tags * sizeof(char *)); + if (!metadata->prog_names) { + bpf_metadata_free(metadata); + return NULL; + } + for (__u32 prog_index =3D 0; prog_index < nr_prog_tags; prog_index++) { + metadata->prog_names[prog_index] =3D zalloc(BPF_PROG_NAME_LEN); + if (!metadata->prog_names[prog_index]) { + bpf_metadata_free(metadata); + return NULL; + } + metadata->nr_prog_names++; + } + + event_size =3D sizeof(metadata->event->bpf_metadata) + + nr_variables * sizeof(metadata->event->bpf_metadata.entries[0]); + metadata->event =3D zalloc(event_size); + if (!metadata->event) { + bpf_metadata_free(metadata); + return NULL; + } + metadata->event->bpf_metadata =3D (struct perf_record_bpf_metadata) { + .header =3D { + .type =3D PERF_RECORD_BPF_METADATA, + .size =3D event_size, + }, + .nr_entries =3D 0, + }; + + return metadata; +} + +static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info) +{ + struct bpf_metadata *metadata; + const __u32 *map_ids =3D (__u32 *)(uintptr_t)info->map_ids; + + for (__u32 map_index =3D 0; map_index < info->nr_map_ids; map_index++) { + struct bpf_metadata_map map; + + if (bpf_metadata_read_map_data(map_ids[map_index], &map) !=3D 0) + continue; + + metadata =3D bpf_metadata_alloc(info->nr_prog_tags, map.num_vars); + if (!metadata) + continue; + + bpf_metadata_fill_event(&map, &metadata->event->bpf_metadata); + + for (__u32 index =3D 0; index < info->nr_prog_tags; index++) { + synthesize_bpf_prog_name(metadata->prog_names[index], + BPF_PROG_NAME_LEN, info, + map.btf, index); + } + + bpf_metadata_free_map_data(&map); + + return metadata; + } + + return NULL; +} + +static int synthesize_perf_record_bpf_metadata(const struct bpf_metadata *= metadata, + const struct perf_tool *tool, + perf_event__handler_t process, + struct machine *machine) +{ + const size_t event_size =3D metadata->event->header.size; + union perf_event *event; + int err =3D 0; + + event =3D zalloc(event_size + machine->id_hdr_size); + if (!event) + return -1; + memcpy(event, metadata->event, event_size); + memset((void *)event + event->header.size, 0, machine->id_hdr_size); + event->header.size +=3D machine->id_hdr_size; + for (__u32 index =3D 0; index < metadata->nr_prog_names; index++) { + memcpy(event->bpf_metadata.prog_name, + metadata->prog_names[index], BPF_PROG_NAME_LEN); + err =3D perf_tool__process_synth_event(tool, event, machine, + process); + if (err !=3D 0) + break; + } + + free(event); + return err; +} + +void bpf_metadata_free(struct bpf_metadata *metadata) +{ + if (metadata =3D=3D NULL) + return; + for (__u32 index =3D 0; index < metadata->nr_prog_names; index++) + free(metadata->prog_names[index]); + free(metadata->prog_names); + free(metadata->event); + free(metadata); +} + +#else /* HAVE_LIBBPF_STRINGS_SUPPORT */ + +static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info= __maybe_unused) +{ + return NULL; +} + +static int synthesize_perf_record_bpf_metadata(const struct bpf_metadata *= metadata __maybe_unused, + const struct perf_tool *tool __maybe_unused, + perf_event__handler_t process __maybe_unused, + struct machine *machine __maybe_unused) +{ + return 0; +} + +void bpf_metadata_free(struct bpf_metadata *metadata __maybe_unused) +{ +} + +#endif /* HAVE_LIBBPF_STRINGS_SUPPORT */ + /* * Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf * program. One PERF_RECORD_BPF_EVENT is generated for the program. And @@ -173,6 +494,7 @@ static int perf_event__synthesize_one_bpf_prog(struct p= erf_session *session, const struct perf_tool *tool =3D session->tool; struct bpf_prog_info_node *info_node; struct perf_bpil *info_linear; + struct bpf_metadata *metadata; struct bpf_prog_info *info; struct btf *btf =3D NULL; struct perf_env *env; @@ -193,6 +515,7 @@ static int perf_event__synthesize_one_bpf_prog(struct p= erf_session *session, arrays |=3D 1UL << PERF_BPIL_JITED_INSNS; arrays |=3D 1UL << PERF_BPIL_LINE_INFO; arrays |=3D 1UL << PERF_BPIL_JITED_LINE_INFO; + arrays |=3D 1UL << PERF_BPIL_MAP_IDS; =20 info_linear =3D get_bpf_prog_info_linear(fd, arrays); if (IS_ERR_OR_NULL(info_linear)) { @@ -301,6 +624,15 @@ static int perf_event__synthesize_one_bpf_prog(struct = perf_session *session, */ err =3D perf_tool__process_synth_event(tool, event, machine, process); + + /* Synthesize PERF_RECORD_BPF_METADATA */ + metadata =3D bpf_metadata_create(info); + if (metadata !=3D NULL) { + err =3D synthesize_perf_record_bpf_metadata(metadata, + tool, process, + machine); + bpf_metadata_free(metadata); + } } =20 out: diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h index e2f0420905f5..ef2dd3f1619e 100644 --- a/tools/perf/util/bpf-event.h +++ b/tools/perf/util/bpf-event.h @@ -17,6 +17,12 @@ struct record_opts; struct evlist; struct target; =20 +struct bpf_metadata { + union perf_event *event; + char **prog_names; + __u64 nr_prog_names; +}; + struct bpf_prog_info_node { struct perf_bpil *info_linear; struct rb_node rb_node; @@ -36,6 +42,7 @@ int evlist__add_bpf_sb_event(struct evlist *evlist, struc= t perf_env *env); void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, struct perf_env *env, FILE *fp); +void bpf_metadata_free(struct bpf_metadata *metadata); #else static inline int machine__process_bpf(struct machine *machine __maybe_unu= sed, union perf_event *event __maybe_unused, @@ -55,6 +62,11 @@ static inline void __bpf_event__print_bpf_prog_info(stru= ct bpf_prog_info *info _ FILE *fp __maybe_unused) { =20 +} + +static inline void bpf_metadata_free(struct bpf_metadata *metadata __maybe= _unused) +{ + } #endif // HAVE_LIBBPF_SUPPORT #endif --=20 2.50.0.rc1.591.g9c95f17f64-goog