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

perf inject: Fix build ID injection

Build ID injection wasn't inserting a sample ID and aligning events to
64 bytes rather than 8. No sample ID means events are unordered and two
different build_id events for the same path, as happens when a file is
replaced, can't be differentiated.

Add in sample ID insertion for the build_id events alongside some
refactoring. The refactoring better aligns the function arguments for
different use cases, such as synthesizing build_id events without
needing to have a dso. The misc bits are explicitly passed as with
callchains the maps/dsos may span user and kernel land, so using
sample->cpumode isn't good enough.

Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Anne Macedo <retpolanne@posteo.net>
Cc: Casey Chen <cachen@purestorage.com>
Cc: Colin Ian King <colin.i.king@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sun Haiyong <sunhaiyong@loongson.cn>
Link: https://lore.kernel.org/r/20240909203740.143492-2-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
ae39ba16 02648783

+175 -55
+131 -39
tools/perf/builtin-inject.c
··· 130 130 struct perf_file_section secs[HEADER_FEAT_BITS]; 131 131 struct guest_session guest_session; 132 132 struct strlist *known_build_ids; 133 + const struct evsel *mmap_evsel; 133 134 }; 134 135 135 136 struct event_entry { ··· 139 138 union perf_event event[]; 140 139 }; 141 140 142 - static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, 143 - struct machine *machine, u8 cpumode, u32 flags); 141 + static int tool__inject_build_id(const struct perf_tool *tool, 142 + struct perf_sample *sample, 143 + struct machine *machine, 144 + const struct evsel *evsel, 145 + __u16 misc, 146 + const char *filename, 147 + struct dso *dso, u32 flags); 144 148 145 149 static int output_bytes(struct perf_inject *inject, void *buf, size_t sz) 146 150 { ··· 428 422 return dso; 429 423 } 430 424 425 + /* 426 + * The evsel used for the sample ID for mmap events. Typically stashed when 427 + * processing mmap events. If not stashed, search the evlist for the first mmap 428 + * gathering event. 429 + */ 430 + static const struct evsel *inject__mmap_evsel(struct perf_inject *inject) 431 + { 432 + struct evsel *pos; 433 + 434 + if (inject->mmap_evsel) 435 + return inject->mmap_evsel; 436 + 437 + evlist__for_each_entry(inject->session->evlist, pos) { 438 + if (pos->core.attr.mmap) { 439 + inject->mmap_evsel = pos; 440 + return pos; 441 + } 442 + } 443 + pr_err("No mmap events found\n"); 444 + return NULL; 445 + } 446 + 431 447 static int perf_event__repipe_common_mmap(const struct perf_tool *tool, 432 448 union perf_event *event, 433 449 struct perf_sample *sample, ··· 497 469 } 498 470 499 471 if (dso && !dso__hit(dso)) { 500 - dso__set_hit(dso); 501 - dso__inject_build_id(dso, tool, machine, sample->cpumode, flags); 472 + struct evsel *evsel = evlist__event2evsel(inject->session->evlist, event); 473 + 474 + if (evsel) { 475 + dso__set_hit(dso); 476 + tool__inject_build_id(tool, sample, machine, evsel, 477 + /*misc=*/sample->cpumode, 478 + filename, dso, flags); 479 + } 502 480 } 503 481 } else { 482 + int err; 483 + 484 + /* 485 + * Remember the evsel for lazy build id generation. It is used 486 + * for the sample id header type. 487 + */ 488 + if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY && 489 + !inject->mmap_evsel) 490 + inject->mmap_evsel = evlist__event2evsel(inject->session->evlist, event); 491 + 504 492 /* Create the thread, map, etc. Not done for the unordered inject all case. */ 505 - int err = perf_event_process(tool, event, sample, machine); 493 + err = perf_event_process(tool, event, sample, machine); 506 494 507 495 if (err) { 508 496 dso__put(dso); ··· 711 667 return false; 712 668 } 713 669 714 - static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, 715 - struct machine *machine, u8 cpumode, u32 flags) 670 + static int tool__inject_build_id(const struct perf_tool *tool, 671 + struct perf_sample *sample, 672 + struct machine *machine, 673 + const struct evsel *evsel, 674 + __u16 misc, 675 + const char *filename, 676 + struct dso *dso, u32 flags) 716 677 { 717 - struct perf_inject *inject = container_of(tool, struct perf_inject, 718 - tool); 678 + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 719 679 int err; 720 680 721 - if (is_anon_memory(dso__long_name(dso)) || flags & MAP_HUGETLB) 681 + if (is_anon_memory(filename) || flags & MAP_HUGETLB) 722 682 return 0; 723 - if (is_no_dso_memory(dso__long_name(dso))) 683 + if (is_no_dso_memory(filename)) 724 684 return 0; 725 685 726 686 if (inject->known_build_ids != NULL && ··· 732 684 return 1; 733 685 734 686 if (dso__read_build_id(dso) < 0) { 735 - pr_debug("no build_id found for %s\n", dso__long_name(dso)); 687 + pr_debug("no build_id found for %s\n", filename); 736 688 return -1; 737 689 } 738 690 739 - err = perf_event__synthesize_build_id(tool, dso, cpumode, 740 - perf_event__repipe, machine); 691 + err = perf_event__synthesize_build_id(tool, sample, machine, 692 + perf_event__repipe, 693 + evsel, misc, dso__bid(dso), 694 + filename); 741 695 if (err) { 742 - pr_err("Can't synthesize build_id event for %s\n", dso__long_name(dso)); 696 + pr_err("Can't synthesize build_id event for %s\n", filename); 743 697 return -1; 744 698 } 745 699 746 700 return 0; 747 701 } 748 702 703 + static int mark_dso_hit(const struct perf_tool *tool, 704 + struct perf_sample *sample, 705 + struct machine *machine, 706 + const struct evsel *mmap_evsel, 707 + struct map *map, bool sample_in_dso) 708 + { 709 + struct dso *dso; 710 + u16 misc = sample->cpumode; 711 + 712 + if (!map) 713 + return 0; 714 + 715 + if (!sample_in_dso) { 716 + u16 guest_mask = PERF_RECORD_MISC_GUEST_KERNEL | 717 + PERF_RECORD_MISC_GUEST_USER; 718 + 719 + if ((misc & guest_mask) != 0) { 720 + misc &= PERF_RECORD_MISC_HYPERVISOR; 721 + misc |= __map__is_kernel(map) 722 + ? PERF_RECORD_MISC_GUEST_KERNEL 723 + : PERF_RECORD_MISC_GUEST_USER; 724 + } else { 725 + misc &= PERF_RECORD_MISC_HYPERVISOR; 726 + misc |= __map__is_kernel(map) 727 + ? PERF_RECORD_MISC_KERNEL 728 + : PERF_RECORD_MISC_USER; 729 + } 730 + } 731 + dso = map__dso(map); 732 + if (dso && !dso__hit(dso)) { 733 + dso__set_hit(dso); 734 + tool__inject_build_id(tool, sample, machine, 735 + mmap_evsel, misc, dso__long_name(dso), dso, 736 + map__flags(map)); 737 + } 738 + return 0; 739 + } 740 + 749 741 struct mark_dso_hit_args { 750 742 const struct perf_tool *tool; 743 + struct perf_sample *sample; 751 744 struct machine *machine; 752 - u8 cpumode; 745 + const struct evsel *mmap_evsel; 753 746 }; 754 747 755 748 static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data) ··· 798 709 struct mark_dso_hit_args *args = data; 799 710 struct map *map = node->ms.map; 800 711 801 - if (map) { 802 - struct dso *dso = map__dso(map); 803 - 804 - if (dso && !dso__hit(dso)) { 805 - dso__set_hit(dso); 806 - dso__inject_build_id(dso, args->tool, args->machine, 807 - args->cpumode, map__flags(map)); 808 - } 809 - } 810 - return 0; 712 + return mark_dso_hit(args->tool, args->sample, args->machine, 713 + args->mmap_evsel, map, /*sample_in_dso=*/false); 811 714 } 812 715 813 716 int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, ··· 809 728 { 810 729 struct addr_location al; 811 730 struct thread *thread; 731 + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); 812 732 struct mark_dso_hit_args args = { 813 733 .tool = tool, 734 + /* 735 + * Use the parsed sample data of the sample event, which will 736 + * have a later timestamp than the mmap event. 737 + */ 738 + .sample = sample, 814 739 .machine = machine, 815 - .cpumode = sample->cpumode, 740 + .mmap_evsel = inject__mmap_evsel(inject), 816 741 }; 817 742 818 743 addr_location__init(&al); ··· 830 743 } 831 744 832 745 if (thread__find_map(thread, sample->cpumode, sample->ip, &al)) { 833 - struct dso *dso = map__dso(al.map); 834 - 835 - if (!dso__hit(dso)) { 836 - dso__set_hit(dso); 837 - dso__inject_build_id(dso, tool, machine, 838 - sample->cpumode, map__flags(al.map)); 839 - } 746 + mark_dso_hit(tool, sample, machine, args.mmap_evsel, al.map, 747 + /*sample_in_dso=*/true); 840 748 } 841 749 842 750 sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, ··· 1241 1159 static int synthesize_build_id(struct perf_inject *inject, struct dso *dso, pid_t machine_pid) 1242 1160 { 1243 1161 struct machine *machine = perf_session__findnew_machine(inject->session, machine_pid); 1244 - u8 cpumode = dso__is_in_kernel_space(dso) ? 1245 - PERF_RECORD_MISC_GUEST_KERNEL : 1246 - PERF_RECORD_MISC_GUEST_USER; 1162 + struct perf_sample synth_sample = { 1163 + .pid = -1, 1164 + .tid = -1, 1165 + .time = -1, 1166 + .stream_id = -1, 1167 + .cpu = -1, 1168 + .period = 1, 1169 + .cpumode = dso__is_in_kernel_space(dso) 1170 + ? PERF_RECORD_MISC_GUEST_KERNEL 1171 + : PERF_RECORD_MISC_GUEST_USER, 1172 + }; 1247 1173 1248 1174 if (!machine) 1249 1175 return -ENOMEM; 1250 1176 1251 1177 dso__set_hit(dso); 1252 1178 1253 - return perf_event__synthesize_build_id(&inject->tool, dso, cpumode, 1254 - process_build_id, machine); 1179 + return perf_event__synthesize_build_id(&inject->tool, &synth_sample, machine, 1180 + process_build_id, inject__mmap_evsel(inject), 1181 + /*misc=*/synth_sample.cpumode, 1182 + dso__bid(dso), dso__long_name(dso)); 1255 1183 } 1256 1184 1257 1185 static int guest_session__add_build_ids_cb(struct dso *dso, void *data)
+3 -3
tools/perf/util/build-id.c
··· 277 277 struct perf_record_header_build_id b; 278 278 size_t len; 279 279 280 - len = name_len + 1; 281 - len = PERF_ALIGN(len, NAME_ALIGN); 280 + len = sizeof(b) + name_len + 1; 281 + len = PERF_ALIGN(len, sizeof(u64)); 282 282 283 283 memset(&b, 0, sizeof(b)); 284 284 memcpy(&b.data, bid->data, bid->size); ··· 286 286 misc |= PERF_RECORD_MISC_BUILD_ID_SIZE; 287 287 b.pid = pid; 288 288 b.header.misc = misc; 289 - b.header.size = sizeof(b) + len; 289 + b.header.size = len; 290 290 291 291 err = do_write(fd, &b, sizeof(b)); 292 292 if (err < 0)
+32 -12
tools/perf/util/synthetic-events.c
··· 2225 2225 } 2226 2226 #endif 2227 2227 2228 - int perf_event__synthesize_build_id(const struct perf_tool *tool, struct dso *pos, u16 misc, 2229 - perf_event__handler_t process, struct machine *machine) 2228 + int perf_event__synthesize_build_id(const struct perf_tool *tool, 2229 + struct perf_sample *sample, 2230 + struct machine *machine, 2231 + perf_event__handler_t process, 2232 + const struct evsel *evsel, 2233 + __u16 misc, 2234 + const struct build_id *bid, 2235 + const char *filename) 2230 2236 { 2231 2237 union perf_event ev; 2232 2238 size_t len; 2233 2239 2234 - if (!dso__hit(pos)) 2235 - return 0; 2240 + len = sizeof(ev.build_id) + strlen(filename) + 1; 2241 + len = PERF_ALIGN(len, sizeof(u64)); 2236 2242 2237 - memset(&ev, 0, sizeof(ev)); 2243 + memset(&ev, 0, len); 2238 2244 2239 - len = dso__long_name_len(pos) + 1; 2240 - len = PERF_ALIGN(len, NAME_ALIGN); 2241 - ev.build_id.size = min(dso__bid(pos)->size, sizeof(dso__bid(pos)->data)); 2242 - memcpy(&ev.build_id.build_id, dso__bid(pos)->data, ev.build_id.size); 2245 + ev.build_id.size = min(bid->size, sizeof(ev.build_id.build_id)); 2246 + memcpy(ev.build_id.build_id, bid->data, ev.build_id.size); 2243 2247 ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; 2244 2248 ev.build_id.header.misc = misc | PERF_RECORD_MISC_BUILD_ID_SIZE; 2245 2249 ev.build_id.pid = machine->pid; 2246 - ev.build_id.header.size = sizeof(ev.build_id) + len; 2247 - memcpy(&ev.build_id.filename, dso__long_name(pos), dso__long_name_len(pos)); 2250 + ev.build_id.header.size = len; 2251 + strcpy(ev.build_id.filename, filename); 2248 2252 2249 - return process(tool, &ev, NULL, machine); 2253 + if (evsel) { 2254 + void *array = &ev; 2255 + int ret; 2256 + 2257 + array += ev.header.size; 2258 + ret = perf_event__synthesize_id_sample(array, evsel->core.attr.sample_type, sample); 2259 + if (ret < 0) 2260 + return ret; 2261 + 2262 + if (ret & 7) { 2263 + pr_err("Bad id sample size %d\n", ret); 2264 + return -EINVAL; 2265 + } 2266 + 2267 + ev.header.size += ret; 2268 + } 2269 + return process(tool, &ev, sample, machine); 2250 2270 } 2251 2271 2252 2272 int perf_event__synthesize_stat_events(struct perf_stat_config *config, const struct perf_tool *tool,
+9 -1
tools/perf/util/synthetic-events.h
··· 9 9 #include <perf/cpumap.h> 10 10 11 11 struct auxtrace_record; 12 + struct build_id; 12 13 struct dso; 13 14 struct evlist; 14 15 struct evsel; ··· 46 45 47 46 int perf_event__synthesize_attrs(const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process); 48 47 int perf_event__synthesize_attr(const struct perf_tool *tool, struct perf_event_attr *attr, u32 ids, u64 *id, perf_event__handler_t process); 49 - int perf_event__synthesize_build_id(const struct perf_tool *tool, struct dso *pos, u16 misc, perf_event__handler_t process, struct machine *machine); 48 + int perf_event__synthesize_build_id(const struct perf_tool *tool, 49 + struct perf_sample *sample, 50 + struct machine *machine, 51 + perf_event__handler_t process, 52 + const struct evsel *evsel, 53 + __u16 misc, 54 + const struct build_id *bid, 55 + const char *filename); 50 56 int perf_event__synthesize_cpu_map(const struct perf_tool *tool, const struct perf_cpu_map *cpus, perf_event__handler_t process, struct machine *machine); 51 57 int perf_event__synthesize_event_update_cpus(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); 52 58 int perf_event__synthesize_event_update_name(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process);