From nobody Fri Dec 19 07:50:52 2025 Received: from mail-pf1-f202.google.com (mail-pf1-f202.google.com [209.85.210.202]) (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 71F39280012 for ; Thu, 5 Jun 2025 23:39:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749166792; cv=none; b=rKPkRyeQ/JQPdu6xYFGTO+/WOwAZP/011bJpQHtb4oRUPP5TBCLh3xUnHSYEP/p74DyUL8owg7WHUE5WBlJXfOi9/XfgKAJBYatawnsFMOFxc8Q7FAWt9Qo+RDeQ7Ewkc5dkURi7zNokMJrlNLO+vb9X3OYq5mC9sU3jkhulWP8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749166792; c=relaxed/simple; bh=lv+bM4moTnKOGlGoG2cAgW1utmnJXzA7idT/X6DTndA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=le9if4P6GvmelbiXlPmLvrU/+bn8JPr0Ji4BdgGNxG+1kBHH0ohIbo7NFkyhvBRIB/gQJkaXpqNGQ7QXKRPJ5f1rBZ3IRec0XpJ5p4oHmP2iRCRVYFlZDDTOZ0CzJzMujhXdoaXtKxVgM09NLrZMr7NyY0Tlmz0d6VQbyLnP4zU= 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=YrClmg8J; arc=none smtp.client-ip=209.85.210.202 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="YrClmg8J" Received: by mail-pf1-f202.google.com with SMTP id d2e1a72fcca58-747dd44048cso1337060b3a.3 for ; Thu, 05 Jun 2025 16:39:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1749166790; x=1749771590; 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=slKzUD48tkq9i10Iz9b2HZco4LcIFVLTatkKbkE7Sdg=; b=YrClmg8JqWjYCnALpEsLRi8oJIcbNNxgL8P5wC6xTUrpxJpGlpe25JDnRshnKUpoKJ X+kwDuAEhsNcjOdnlhEshqFEH1SI1nI/CW84Odqu28tnFpG3UR5tSaRj82x8OZgOD6CC uo4s9AOVHCiAFyKaq6m5vG32b+Hx7cEbgIrrI3BWSSrlU8QfuAfX3kCJjaTFoIqcYdCV nhgXY5J79JcyZYdmsAjCA1pMewKu4obJVjhvCuel7rYoogmON/qGZ5AuB0gS0ZVs9tn5 PbVXoP+OnMCZvycY3igr8wDqY03BPevXNcK/eaX20f++7nlVAJB0N67heDO1oVjwQyrH jxWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749166790; x=1749771590; 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=slKzUD48tkq9i10Iz9b2HZco4LcIFVLTatkKbkE7Sdg=; b=A7OroBvGnlQ6D6KUfz5AJ2DT03/sQlwwGxWJKqNeuXA0dYAMDjDkQDZZQsv7hwJAfi d4xRC7WrhHMEQdke37HW4IhDg0Xuaq/472bdNc0iYjzsPO5hr56k1EaeuOnsJU3vHafC VkXh4CWJAsChgAz+/ZyqY5EUR49nVoaZNfzLmmYeF7csqxPnW5AxSBR6Wh3swOtzoxmb gAnprGBbwGIZAa5kjSDhzCAFvjk84FSdbfq5Y7R15wRN16scWN78ArLBiU5h9FIX5ban SVe7Smbuldl6NMOKT8EdkWAS3c1kkyya8iis8pJL36SrmUI2/dvg+OTF2ft4p1gscAmG bX8A== X-Forwarded-Encrypted: i=1; AJvYcCV0Kk9j3BITgLAt5xhwKZ1zEDHIXvLkVeR4+N1cs2/VxEGiTjwXgRSRNGfw/aCxnOxi8FqkxZHLsQTmi8k=@vger.kernel.org X-Gm-Message-State: AOJu0YwRkxKmGe1qN3XBTF36jz/QaRXurwzbNOqmU7dQPoKFl5fZ8WYL R4Jujb7SYZiSHxhLi2wdAmbo0AwTkgaielhXIEK4nGHHeNgX0xZleu+dtPpW8gO8GIn18gSF8If pUPA3WMLRs8Q7nt08aSVRxg== X-Google-Smtp-Source: AGHT+IGRn75rA9+CWGF+pQ2bmargynx83ZVWyLHO26zHNwn+Nq9+SnbPPTD34vfcFptZCQEfH8DZhKZqHVvDlAyU X-Received: from pfbhm22.prod.google.com ([2002:a05:6a00:6716:b0:746:2bf3:e5e8]) (user=blakejones job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:2586:b0:1f3:2e85:c052 with SMTP id adf61e73a8af0-21ee32b637bmr1528499637.35.1749166789748; Thu, 05 Jun 2025 16:39:49 -0700 (PDT) Date: Thu, 5 Jun 2025 16:39:32 -0700 In-Reply-To: <20250605233934.1881839-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: <20250605233934.1881839-1-blakejones@google.com> X-Mailer: git-send-email 2.50.0.rc0.604.gd4ff7b7c86-goog Message-ID: <20250605233934.1881839-3-blakejones@google.com> Subject: [PATCH v2 2/4] 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..16644b3aaba1 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) +{ + } #endif // HAVE_LIBBPF_SUPPORT #endif --=20 2.50.0.rc0.604.gd4ff7b7c86-goog