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

perf bench: Add build-id injection benchmark

Sometimes I can see that 'perf record' piped with 'perf inject' take a
long time processing build-ids.

So introduce a inject-build-id benchmark to the internals benchmark
suite to measure its overhead regularly.

It runs the 'perf inject' command internally and feeds the given number
of synthesized events (MMAP2 + SAMPLE basically).

Usage: perf bench internals inject-build-id <options>

-i, --iterations <n> Number of iterations used to compute average (default: 100)
-m, --nr-mmaps <n> Number of mmap events for each iteration (default: 100)
-n, --nr-samples <n> Number of sample events per mmap event (default: 100)
-v, --verbose be more verbose (show iteration count, DSO name, etc)

By default, it measures average processing time of 100 MMAP2 events
and 10000 SAMPLE events. Below is a result on my laptop.

$ perf bench internals inject-build-id
# Running 'internals/inject-build-id' benchmark:
Average build-id injection took: 25.789 msec (+- 0.202 msec)
Average time per event: 2.528 usec (+- 0.020 usec)
Average memory usage: 8411 KB (+- 7 KB)

Committer testing:

$ perf bench
Usage:
perf bench [<common options>] <collection> <benchmark> [<options>]

# List of all available benchmark collections:

sched: Scheduler and IPC benchmarks
syscall: System call benchmarks
mem: Memory access benchmarks
numa: NUMA scheduling and MM benchmarks
futex: Futex stressing benchmarks
epoll: Epoll stressing benchmarks
internals: Perf-internals benchmarks
all: All benchmarks

$ perf bench internals

# List of available benchmarks for collection 'internals':

synthesize: Benchmark perf event synthesis
kallsyms-parse: Benchmark kallsyms parsing
inject-build-id: Benchmark build-id injection

$ perf bench internals inject-build-id
# Running 'internals/inject-build-id' benchmark:
Average build-id injection took: 14.202 msec (+- 0.059 msec)
Average time per event: 1.392 usec (+- 0.006 usec)
Average memory usage: 12650 KB (+- 10 KB)
Average build-id-all injection took: 12.831 msec (+- 0.071 msec)
Average time per event: 1.258 usec (+- 0.007 usec)
Average memory usage: 11895 KB (+- 10 KB)
$

$ perf stat -r5 perf bench internals inject-build-id
# Running 'internals/inject-build-id' benchmark:
Average build-id injection took: 14.380 msec (+- 0.056 msec)
Average time per event: 1.410 usec (+- 0.006 usec)
Average memory usage: 12608 KB (+- 11 KB)
Average build-id-all injection took: 11.889 msec (+- 0.064 msec)
Average time per event: 1.166 usec (+- 0.006 usec)
Average memory usage: 11838 KB (+- 10 KB)
# Running 'internals/inject-build-id' benchmark:
Average build-id injection took: 14.246 msec (+- 0.065 msec)
Average time per event: 1.397 usec (+- 0.006 usec)
Average memory usage: 12744 KB (+- 10 KB)
Average build-id-all injection took: 12.019 msec (+- 0.066 msec)
Average time per event: 1.178 usec (+- 0.006 usec)
Average memory usage: 11963 KB (+- 10 KB)
# Running 'internals/inject-build-id' benchmark:
Average build-id injection took: 14.321 msec (+- 0.067 msec)
Average time per event: 1.404 usec (+- 0.007 usec)
Average memory usage: 12690 KB (+- 10 KB)
Average build-id-all injection took: 11.909 msec (+- 0.041 msec)
Average time per event: 1.168 usec (+- 0.004 usec)
Average memory usage: 11938 KB (+- 10 KB)
# Running 'internals/inject-build-id' benchmark:
Average build-id injection took: 14.287 msec (+- 0.059 msec)
Average time per event: 1.401 usec (+- 0.006 usec)
Average memory usage: 12864 KB (+- 10 KB)
Average build-id-all injection took: 11.862 msec (+- 0.058 msec)
Average time per event: 1.163 usec (+- 0.006 usec)
Average memory usage: 12103 KB (+- 10 KB)
# Running 'internals/inject-build-id' benchmark:
Average build-id injection took: 14.402 msec (+- 0.053 msec)
Average time per event: 1.412 usec (+- 0.005 usec)
Average memory usage: 12876 KB (+- 10 KB)
Average build-id-all injection took: 11.826 msec (+- 0.061 msec)
Average time per event: 1.159 usec (+- 0.006 usec)
Average memory usage: 12111 KB (+- 10 KB)

Performance counter stats for 'perf bench internals inject-build-id' (5 runs):

4,267.48 msec task-clock:u # 1.502 CPUs utilized ( +- 0.14% )
0 context-switches:u # 0.000 K/sec
0 cpu-migrations:u # 0.000 K/sec
102,092 page-faults:u # 0.024 M/sec ( +- 0.08% )
3,894,589,578 cycles:u # 0.913 GHz ( +- 0.19% ) (83.49%)
140,078,421 stalled-cycles-frontend:u # 3.60% frontend cycles idle ( +- 0.77% ) (83.34%)
948,581,189 stalled-cycles-backend:u # 24.36% backend cycles idle ( +- 0.46% ) (83.25%)
5,835,587,719 instructions:u # 1.50 insn per cycle
# 0.16 stalled cycles per insn ( +- 0.21% ) (83.24%)
1,267,423,636 branches:u # 296.996 M/sec ( +- 0.22% ) (83.12%)
17,484,290 branch-misses:u # 1.38% of all branches ( +- 0.12% ) (83.55%)

2.84176 +- 0.00222 seconds time elapsed ( +- 0.08% )

$

Acked-by: Jiri Olsa <jolsa@redhat.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20201012070214.2074921-2-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
0bf02a0d 388968d8

+471 -5
+1
tools/perf/bench/Build
··· 12 12 perf-y += synthesize.o 13 13 perf-y += kallsyms-parse.o 14 14 perf-y += find-bit-bench.o 15 + perf-y += inject-buildid.o 15 16 16 17 perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-lib.o 17 18 perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
+1
tools/perf/bench/bench.h
··· 47 47 int bench_epoll_ctl(int argc, const char **argv); 48 48 int bench_synthesize(int argc, const char **argv); 49 49 int bench_kallsyms_parse(int argc, const char **argv); 50 + int bench_inject_build_id(int argc, const char **argv); 50 51 51 52 #define BENCH_FORMAT_DEFAULT_STR "default" 52 53 #define BENCH_FORMAT_DEFAULT 0
+460
tools/perf/bench/inject-buildid.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <stdlib.h> 3 + #include <stddef.h> 4 + #include <ftw.h> 5 + #include <fcntl.h> 6 + #include <errno.h> 7 + #include <unistd.h> 8 + #include <pthread.h> 9 + #include <sys/mman.h> 10 + #include <sys/wait.h> 11 + #include <linux/kernel.h> 12 + #include <linux/time64.h> 13 + #include <linux/list.h> 14 + #include <linux/err.h> 15 + #include <internal/lib.h> 16 + #include <subcmd/parse-options.h> 17 + 18 + #include "bench.h" 19 + #include "util/data.h" 20 + #include "util/stat.h" 21 + #include "util/debug.h" 22 + #include "util/event.h" 23 + #include "util/symbol.h" 24 + #include "util/session.h" 25 + #include "util/build-id.h" 26 + #include "util/synthetic-events.h" 27 + 28 + #define MMAP_DEV_MAJOR 8 29 + #define DSO_MMAP_RATIO 4 30 + 31 + static unsigned int iterations = 100; 32 + static unsigned int nr_mmaps = 100; 33 + static unsigned int nr_samples = 100; /* samples per mmap */ 34 + 35 + static u64 bench_sample_type; 36 + static u16 bench_id_hdr_size; 37 + 38 + struct bench_data { 39 + int pid; 40 + int input_pipe[2]; 41 + int output_pipe[2]; 42 + pthread_t th; 43 + }; 44 + 45 + struct bench_dso { 46 + struct list_head list; 47 + char *name; 48 + int ino; 49 + }; 50 + 51 + static int nr_dsos; 52 + static struct bench_dso *dsos; 53 + 54 + extern int cmd_inject(int argc, const char *argv[]); 55 + 56 + static const struct option options[] = { 57 + OPT_UINTEGER('i', "iterations", &iterations, 58 + "Number of iterations used to compute average (default: 100)"), 59 + OPT_UINTEGER('m', "nr-mmaps", &nr_mmaps, 60 + "Number of mmap events for each iteration (default: 100)"), 61 + OPT_UINTEGER('n', "nr-samples", &nr_samples, 62 + "Number of sample events per mmap event (default: 100)"), 63 + OPT_INCR('v', "verbose", &verbose, 64 + "be more verbose (show iteration count, DSO name, etc)"), 65 + OPT_END() 66 + }; 67 + 68 + static const char *const bench_usage[] = { 69 + "perf bench internals inject-build-id <options>", 70 + NULL 71 + }; 72 + 73 + /* 74 + * Helper for collect_dso that adds the given file as a dso to dso_list 75 + * if it contains a build-id. Stops after collecting 4 times more than 76 + * we need (for MMAP2 events). 77 + */ 78 + static int add_dso(const char *fpath, const struct stat *sb __maybe_unused, 79 + int typeflag, struct FTW *ftwbuf __maybe_unused) 80 + { 81 + struct bench_dso *dso = &dsos[nr_dsos]; 82 + unsigned char build_id[BUILD_ID_SIZE]; 83 + 84 + if (typeflag == FTW_D || typeflag == FTW_SL) 85 + return 0; 86 + 87 + if (filename__read_build_id(fpath, build_id, BUILD_ID_SIZE) < 0) 88 + return 0; 89 + 90 + dso->name = realpath(fpath, NULL); 91 + if (dso->name == NULL) 92 + return -1; 93 + 94 + dso->ino = nr_dsos++; 95 + pr_debug2(" Adding DSO: %s\n", fpath); 96 + 97 + /* stop if we collected enough DSOs */ 98 + if ((unsigned int)nr_dsos == DSO_MMAP_RATIO * nr_mmaps) 99 + return 1; 100 + 101 + return 0; 102 + } 103 + 104 + static void collect_dso(void) 105 + { 106 + dsos = calloc(nr_mmaps * DSO_MMAP_RATIO, sizeof(*dsos)); 107 + if (dsos == NULL) { 108 + printf(" Memory allocation failed\n"); 109 + exit(1); 110 + } 111 + 112 + if (nftw("/usr/lib/", add_dso, 10, FTW_PHYS) < 0) 113 + return; 114 + 115 + pr_debug(" Collected %d DSOs\n", nr_dsos); 116 + } 117 + 118 + static void release_dso(void) 119 + { 120 + int i; 121 + 122 + for (i = 0; i < nr_dsos; i++) { 123 + struct bench_dso *dso = &dsos[i]; 124 + 125 + free(dso->name); 126 + } 127 + free(dsos); 128 + } 129 + 130 + /* Fake address used by mmap and sample events */ 131 + static u64 dso_map_addr(struct bench_dso *dso) 132 + { 133 + return 0x400000ULL + dso->ino * 8192ULL; 134 + } 135 + 136 + static u32 synthesize_attr(struct bench_data *data) 137 + { 138 + union perf_event event; 139 + 140 + memset(&event, 0, sizeof(event.attr) + sizeof(u64)); 141 + 142 + event.header.type = PERF_RECORD_HEADER_ATTR; 143 + event.header.size = sizeof(event.attr) + sizeof(u64); 144 + 145 + event.attr.attr.type = PERF_TYPE_SOFTWARE; 146 + event.attr.attr.config = PERF_COUNT_SW_TASK_CLOCK; 147 + event.attr.attr.exclude_kernel = 1; 148 + event.attr.attr.sample_id_all = 1; 149 + event.attr.attr.sample_type = bench_sample_type; 150 + 151 + return writen(data->input_pipe[1], &event, event.header.size); 152 + } 153 + 154 + static u32 synthesize_fork(struct bench_data *data) 155 + { 156 + union perf_event event; 157 + 158 + memset(&event, 0, sizeof(event.fork) + bench_id_hdr_size); 159 + 160 + event.header.type = PERF_RECORD_FORK; 161 + event.header.misc = PERF_RECORD_MISC_FORK_EXEC; 162 + event.header.size = sizeof(event.fork) + bench_id_hdr_size; 163 + 164 + event.fork.ppid = 1; 165 + event.fork.ptid = 1; 166 + event.fork.pid = data->pid; 167 + event.fork.tid = data->pid; 168 + 169 + return writen(data->input_pipe[1], &event, event.header.size); 170 + } 171 + 172 + static u32 synthesize_mmap(struct bench_data *data, struct bench_dso *dso, 173 + u64 timestamp) 174 + { 175 + union perf_event event; 176 + size_t len = offsetof(struct perf_record_mmap2, filename); 177 + u64 *id_hdr_ptr = (void *)&event; 178 + int ts_idx; 179 + 180 + len += roundup(strlen(dso->name) + 1, 8) + bench_id_hdr_size; 181 + 182 + memset(&event, 0, min(len, sizeof(event.mmap2))); 183 + 184 + event.header.type = PERF_RECORD_MMAP2; 185 + event.header.misc = PERF_RECORD_MISC_USER; 186 + event.header.size = len; 187 + 188 + event.mmap2.pid = data->pid; 189 + event.mmap2.tid = data->pid; 190 + event.mmap2.maj = MMAP_DEV_MAJOR; 191 + event.mmap2.ino = dso->ino; 192 + 193 + strcpy(event.mmap2.filename, dso->name); 194 + 195 + event.mmap2.start = dso_map_addr(dso); 196 + event.mmap2.len = 4096; 197 + event.mmap2.prot = PROT_EXEC; 198 + 199 + if (len > sizeof(event.mmap2)) { 200 + /* write mmap2 event first */ 201 + writen(data->input_pipe[1], &event, len - bench_id_hdr_size); 202 + /* zero-fill sample id header */ 203 + memset(id_hdr_ptr, 0, bench_id_hdr_size); 204 + /* put timestamp in the right position */ 205 + ts_idx = (bench_id_hdr_size / sizeof(u64)) - 2; 206 + id_hdr_ptr[ts_idx] = timestamp; 207 + writen(data->input_pipe[1], id_hdr_ptr, bench_id_hdr_size); 208 + } else { 209 + ts_idx = (len / sizeof(u64)) - 2; 210 + id_hdr_ptr[ts_idx] = timestamp; 211 + writen(data->input_pipe[1], &event, len); 212 + } 213 + return len; 214 + } 215 + 216 + static u32 synthesize_sample(struct bench_data *data, struct bench_dso *dso, 217 + u64 timestamp) 218 + { 219 + union perf_event event; 220 + struct perf_sample sample = { 221 + .tid = data->pid, 222 + .pid = data->pid, 223 + .ip = dso_map_addr(dso), 224 + .time = timestamp, 225 + }; 226 + 227 + event.header.type = PERF_RECORD_SAMPLE; 228 + event.header.misc = PERF_RECORD_MISC_USER; 229 + event.header.size = perf_event__sample_event_size(&sample, bench_sample_type, 0); 230 + 231 + perf_event__synthesize_sample(&event, bench_sample_type, 0, &sample); 232 + 233 + return writen(data->input_pipe[1], &event, event.header.size); 234 + } 235 + 236 + static u32 synthesize_flush(struct bench_data *data) 237 + { 238 + struct perf_event_header header = { 239 + .size = sizeof(header), 240 + .type = PERF_RECORD_FINISHED_ROUND, 241 + }; 242 + 243 + return writen(data->input_pipe[1], &header, header.size); 244 + } 245 + 246 + static void *data_reader(void *arg) 247 + { 248 + struct bench_data *data = arg; 249 + char buf[8192]; 250 + int flag; 251 + int n; 252 + 253 + flag = fcntl(data->output_pipe[0], F_GETFL); 254 + fcntl(data->output_pipe[0], F_SETFL, flag | O_NONBLOCK); 255 + 256 + /* read out data from child */ 257 + while (true) { 258 + n = read(data->output_pipe[0], buf, sizeof(buf)); 259 + if (n > 0) 260 + continue; 261 + if (n == 0) 262 + break; 263 + 264 + if (errno != EINTR && errno != EAGAIN) 265 + break; 266 + 267 + usleep(100); 268 + } 269 + 270 + close(data->output_pipe[0]); 271 + return NULL; 272 + } 273 + 274 + static int setup_injection(struct bench_data *data) 275 + { 276 + int ready_pipe[2]; 277 + int dev_null_fd; 278 + char buf; 279 + 280 + if (pipe(ready_pipe) < 0) 281 + return -1; 282 + 283 + if (pipe(data->input_pipe) < 0) 284 + return -1; 285 + 286 + if (pipe(data->output_pipe) < 0) 287 + return -1; 288 + 289 + data->pid = fork(); 290 + if (data->pid < 0) 291 + return -1; 292 + 293 + if (data->pid == 0) { 294 + const char **inject_argv; 295 + 296 + close(data->input_pipe[1]); 297 + close(data->output_pipe[0]); 298 + close(ready_pipe[0]); 299 + 300 + dup2(data->input_pipe[0], STDIN_FILENO); 301 + close(data->input_pipe[0]); 302 + dup2(data->output_pipe[1], STDOUT_FILENO); 303 + close(data->output_pipe[1]); 304 + 305 + dev_null_fd = open("/dev/null", O_WRONLY); 306 + if (dev_null_fd < 0) 307 + exit(1); 308 + 309 + dup2(dev_null_fd, STDERR_FILENO); 310 + 311 + inject_argv = calloc(3, sizeof(*inject_argv)); 312 + if (inject_argv == NULL) 313 + exit(1); 314 + 315 + inject_argv[0] = strdup("inject"); 316 + inject_argv[1] = strdup("-b"); 317 + 318 + /* signal that we're ready to go */ 319 + close(ready_pipe[1]); 320 + 321 + cmd_inject(2, inject_argv); 322 + 323 + exit(0); 324 + } 325 + 326 + pthread_create(&data->th, NULL, data_reader, data); 327 + 328 + close(ready_pipe[1]); 329 + close(data->input_pipe[0]); 330 + close(data->output_pipe[1]); 331 + 332 + /* wait for child ready */ 333 + if (read(ready_pipe[0], &buf, 1) < 0) 334 + return -1; 335 + close(ready_pipe[0]); 336 + 337 + return 0; 338 + } 339 + 340 + static int inject_build_id(struct bench_data *data, u64 *max_rss) 341 + { 342 + int status; 343 + unsigned int i, k; 344 + struct rusage rusage; 345 + u64 len = 0; 346 + 347 + /* this makes the child to run */ 348 + if (perf_header__write_pipe(data->input_pipe[1]) < 0) 349 + return -1; 350 + 351 + len += synthesize_attr(data); 352 + len += synthesize_fork(data); 353 + 354 + for (i = 0; i < nr_mmaps; i++) { 355 + int idx = rand() % (nr_dsos - 1); 356 + struct bench_dso *dso = &dsos[idx]; 357 + u64 timestamp = rand() % 1000000; 358 + 359 + pr_debug2(" [%d] injecting: %s\n", i+1, dso->name); 360 + len += synthesize_mmap(data, dso, timestamp); 361 + 362 + for (k = 0; k < nr_samples; k++) 363 + len += synthesize_sample(data, dso, timestamp + k * 1000); 364 + 365 + if ((i + 1) % 10 == 0) 366 + len += synthesize_flush(data); 367 + } 368 + 369 + /* tihs makes the child to finish */ 370 + close(data->input_pipe[1]); 371 + 372 + wait4(data->pid, &status, 0, &rusage); 373 + *max_rss = rusage.ru_maxrss; 374 + 375 + pr_debug(" Child %d exited with %d\n", data->pid, status); 376 + 377 + return 0; 378 + } 379 + 380 + static int do_inject_loop(struct bench_data *data) 381 + { 382 + unsigned int i; 383 + struct stats time_stats, mem_stats; 384 + double time_average, time_stddev; 385 + double mem_average, mem_stddev; 386 + 387 + srand(time(NULL)); 388 + init_stats(&time_stats); 389 + init_stats(&mem_stats); 390 + symbol__init(NULL); 391 + 392 + bench_sample_type = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP; 393 + bench_sample_type |= PERF_SAMPLE_TID | PERF_SAMPLE_TIME; 394 + bench_id_hdr_size = 32; 395 + 396 + collect_dso(); 397 + if (nr_dsos == 0) { 398 + printf(" Cannot collect DSOs for injection\n"); 399 + return -1; 400 + } 401 + 402 + for (i = 0; i < iterations; i++) { 403 + struct timeval start, end, diff; 404 + u64 runtime_us, max_rss; 405 + 406 + pr_debug(" Iteration #%d\n", i+1); 407 + 408 + if (setup_injection(data) < 0) { 409 + printf(" Build-id injection setup failed\n"); 410 + break; 411 + } 412 + 413 + gettimeofday(&start, NULL); 414 + if (inject_build_id(data, &max_rss) < 0) { 415 + printf(" Build-id injection failed\n"); 416 + break; 417 + } 418 + 419 + gettimeofday(&end, NULL); 420 + timersub(&end, &start, &diff); 421 + runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; 422 + update_stats(&time_stats, runtime_us); 423 + update_stats(&mem_stats, max_rss); 424 + 425 + pthread_join(data->th, NULL); 426 + } 427 + 428 + time_average = avg_stats(&time_stats) / USEC_PER_MSEC; 429 + time_stddev = stddev_stats(&time_stats) / USEC_PER_MSEC; 430 + printf(" Average build-id injection took: %.3f msec (+- %.3f msec)\n", 431 + time_average, time_stddev); 432 + 433 + /* each iteration, it processes MMAP2 + BUILD_ID + nr_samples * SAMPLE */ 434 + time_average = avg_stats(&time_stats) / (nr_mmaps * (nr_samples + 2)); 435 + time_stddev = stddev_stats(&time_stats) / (nr_mmaps * (nr_samples + 2)); 436 + printf(" Average time per event: %.3f usec (+- %.3f usec)\n", 437 + time_average, time_stddev); 438 + 439 + mem_average = avg_stats(&mem_stats); 440 + mem_stddev = stddev_stats(&mem_stats); 441 + printf(" Average memory usage: %.0f KB (+- %.0f KB)\n", 442 + mem_average, mem_stddev); 443 + 444 + release_dso(); 445 + return 0; 446 + } 447 + 448 + int bench_inject_build_id(int argc, const char **argv) 449 + { 450 + struct bench_data data; 451 + 452 + argc = parse_options(argc, argv, options, bench_usage, 0); 453 + if (argc) { 454 + usage_with_options(bench_usage, options); 455 + exit(EXIT_FAILURE); 456 + } 457 + 458 + return do_inject_loop(&data); 459 + } 460 +
+1
tools/perf/builtin-bench.c
··· 87 87 static struct bench internals_benchmarks[] = { 88 88 { "synthesize", "Benchmark perf event synthesis", bench_synthesize }, 89 89 { "kallsyms-parse", "Benchmark kallsyms parsing", bench_kallsyms_parse }, 90 + { "inject-build-id", "Benchmark build-id injection", bench_inject_build_id }, 90 91 { NULL, NULL, NULL } 91 92 }; 92 93
+4 -5
tools/perf/builtin-inject.c
··· 441 441 return 0; 442 442 } 443 443 444 - static int perf_event__inject_buildid(struct perf_tool *tool, 445 - union perf_event *event, 446 - struct perf_sample *sample, 447 - struct evsel *evsel __maybe_unused, 448 - struct machine *machine) 444 + int perf_event__inject_buildid(struct perf_tool *tool, union perf_event *event, 445 + struct perf_sample *sample, 446 + struct evsel *evsel __maybe_unused, 447 + struct machine *machine) 449 448 { 450 449 struct addr_location al; 451 450 struct thread *thread;
+4
tools/perf/util/build-id.h
··· 29 29 30 30 int dsos__hit_all(struct perf_session *session); 31 31 32 + int perf_event__inject_buildid(struct perf_tool *tool, union perf_event *event, 33 + struct perf_sample *sample, struct evsel *evsel, 34 + struct machine *machine); 35 + 32 36 bool perf_session__read_build_ids(struct perf_session *session, bool with_hits); 33 37 int perf_session__write_buildid_table(struct perf_session *session, 34 38 struct feat_fd *fd);