Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf record: collect BPF metadata from new programs

This collects metadata for any BPF programs that were loaded during a
"perf record" run, and emits it at the end of the run.

Signed-off-by: Blake Jones <blakejones@google.com>
Link: https://lore.kernel.org/r/20250612194939.162730-4-blakejones@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Blake Jones and committed by
Namhyung Kim
fdc3441f ab38e84b

+84 -1
+10
tools/perf/builtin-record.c
··· 2162 2162 return err; 2163 2163 } 2164 2164 2165 + static void record__synthesize_final_bpf_metadata(struct record *rec __maybe_unused) 2166 + { 2167 + #ifdef HAVE_LIBBPF_SUPPORT 2168 + perf_event__synthesize_final_bpf_metadata(rec->session, 2169 + process_synthesized_event); 2170 + #endif 2171 + } 2172 + 2165 2173 static int record__process_signal_event(union perf_event *event __maybe_unused, void *data) 2166 2174 { 2167 2175 struct record *rec = data; ··· 2814 2806 2815 2807 trigger_off(&auxtrace_snapshot_trigger); 2816 2808 trigger_off(&switch_output_trigger); 2809 + 2810 + record__synthesize_final_bpf_metadata(rec); 2817 2811 2818 2812 if (opts->auxtrace_snapshot_on_exit) 2819 2813 record__auxtrace_snapshot_exit(rec);
+46
tools/perf/util/bpf-event.c
··· 472 472 473 473 #endif /* HAVE_LIBBPF_STRINGS_SUPPORT */ 474 474 475 + struct bpf_metadata_final_ctx { 476 + const struct perf_tool *tool; 477 + perf_event__handler_t process; 478 + struct machine *machine; 479 + }; 480 + 481 + static void synthesize_final_bpf_metadata_cb(struct bpf_prog_info_node *node, 482 + void *data) 483 + { 484 + struct bpf_metadata_final_ctx *ctx = (struct bpf_metadata_final_ctx *)data; 485 + struct bpf_metadata *metadata = node->metadata; 486 + int err; 487 + 488 + if (metadata == NULL) 489 + return; 490 + err = synthesize_perf_record_bpf_metadata(metadata, ctx->tool, 491 + ctx->process, ctx->machine); 492 + if (err != 0) { 493 + const char *prog_name = metadata->prog_names[0]; 494 + 495 + if (prog_name != NULL) 496 + pr_warning("Couldn't synthesize final BPF metadata for %s.\n", prog_name); 497 + else 498 + pr_warning("Couldn't synthesize final BPF metadata.\n"); 499 + } 500 + bpf_metadata_free(metadata); 501 + node->metadata = NULL; 502 + } 503 + 504 + void perf_event__synthesize_final_bpf_metadata(struct perf_session *session, 505 + perf_event__handler_t process) 506 + { 507 + struct perf_env *env = &session->header.env; 508 + struct bpf_metadata_final_ctx ctx = { 509 + .tool = session->tool, 510 + .process = process, 511 + .machine = &session->machines.host, 512 + }; 513 + 514 + perf_env__iterate_bpf_prog_info(env, synthesize_final_bpf_metadata_cb, 515 + &ctx); 516 + } 517 + 475 518 /* 476 519 * Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf 477 520 * program. One PERF_RECORD_BPF_EVENT is generated for the program. And ··· 655 612 } 656 613 657 614 info_node->info_linear = info_linear; 615 + info_node->metadata = NULL; 658 616 if (!perf_env__insert_bpf_prog_info(env, info_node)) { 659 617 free(info_linear); 660 618 free(info_node); ··· 847 803 arrays |= 1UL << PERF_BPIL_JITED_INSNS; 848 804 arrays |= 1UL << PERF_BPIL_LINE_INFO; 849 805 arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO; 806 + arrays |= 1UL << PERF_BPIL_MAP_IDS; 850 807 851 808 info_linear = get_bpf_prog_info_linear(fd, arrays); 852 809 if (IS_ERR_OR_NULL(info_linear)) { ··· 860 815 info_node = malloc(sizeof(struct bpf_prog_info_node)); 861 816 if (info_node) { 862 817 info_node->info_linear = info_linear; 818 + info_node->metadata = bpf_metadata_create(&info_linear->info); 863 819 if (!perf_env__insert_bpf_prog_info(env, info_node)) { 864 820 free(info_linear); 865 821 free(info_node);
+1
tools/perf/util/bpf-event.h
··· 25 25 26 26 struct bpf_prog_info_node { 27 27 struct perf_bpil *info_linear; 28 + struct bpf_metadata *metadata; 28 29 struct rb_node rb_node; 29 30 }; 30 31
+18 -1
tools/perf/util/env.c
··· 3 3 #include "debug.h" 4 4 #include "env.h" 5 5 #include "util/header.h" 6 - #include "linux/compiler.h" 6 + #include "util/rwsem.h" 7 + #include <linux/compiler.h> 7 8 #include <linux/ctype.h> 9 + #include <linux/rbtree.h> 8 10 #include <linux/string.h> 9 11 #include <linux/zalloc.h> 10 12 #include "cgroup.h" ··· 89 87 out: 90 88 up_read(&env->bpf_progs.lock); 91 89 return node; 90 + } 91 + 92 + void perf_env__iterate_bpf_prog_info(struct perf_env *env, 93 + void (*cb)(struct bpf_prog_info_node *node, 94 + void *data), 95 + void *data) 96 + { 97 + struct rb_node *first; 98 + 99 + down_read(&env->bpf_progs.lock); 100 + first = rb_first(&env->bpf_progs.infos); 101 + for (struct rb_node *node = first; node != NULL; node = rb_next(node)) 102 + (*cb)(rb_entry(node, struct bpf_prog_info_node, rb_node), data); 103 + up_read(&env->bpf_progs.lock); 92 104 } 93 105 94 106 bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node) ··· 190 174 next = rb_next(&node->rb_node); 191 175 rb_erase(&node->rb_node, root); 192 176 zfree(&node->info_linear); 177 + bpf_metadata_free(node->metadata); 193 178 free(node); 194 179 } 195 180
+6
tools/perf/util/env.h
··· 174 174 int perf_env__nr_cpus_avail(struct perf_env *env); 175 175 176 176 void perf_env__init(struct perf_env *env); 177 + #ifdef HAVE_LIBBPF_SUPPORT 177 178 bool __perf_env__insert_bpf_prog_info(struct perf_env *env, 178 179 struct bpf_prog_info_node *info_node); 179 180 bool perf_env__insert_bpf_prog_info(struct perf_env *env, 180 181 struct bpf_prog_info_node *info_node); 181 182 struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env, 182 183 __u32 prog_id); 184 + void perf_env__iterate_bpf_prog_info(struct perf_env *env, 185 + void (*cb)(struct bpf_prog_info_node *node, 186 + void *data), 187 + void *data); 183 188 bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node); 184 189 bool __perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node); 185 190 struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id); 186 191 struct btf_node *__perf_env__find_btf(struct perf_env *env, __u32 btf_id); 192 + #endif // HAVE_LIBBPF_SUPPORT 187 193 188 194 int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu); 189 195 char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name,
+1
tools/perf/util/header.c
··· 3145 3145 /* after reading from file, translate offset to address */ 3146 3146 bpil_offs_to_addr(info_linear); 3147 3147 info_node->info_linear = info_linear; 3148 + info_node->metadata = NULL; 3148 3149 if (!__perf_env__insert_bpf_prog_info(env, info_node)) { 3149 3150 free(info_linear); 3150 3151 free(info_node);
+2
tools/perf/util/synthetic-events.h
··· 92 92 int perf_event__synthesize_tracing_data(const struct perf_tool *tool, int fd, struct evlist *evlist, perf_event__handler_t process); 93 93 int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); 94 94 pid_t perf_event__synthesize_comm(const struct perf_tool *tool, union perf_event *event, pid_t pid, perf_event__handler_t process, struct machine *machine); 95 + void perf_event__synthesize_final_bpf_metadata(struct perf_session *session, 96 + perf_event__handler_t process); 95 97 96 98 int perf_tool__process_synth_event(const struct perf_tool *tool, union perf_event *event, struct machine *machine, perf_event__handler_t process); 97 99