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

Configure Feed

Select the types of activity you want to include in your feed.

perf unwind-libdw: Don't discard loaded ELF/DWARF after every unwind

The unwind-libdw dwfl has ELF binaries associated with mmap
addresses. Experimenting with using the per dso dwfl it is required to
alter the address to be 0 based variant. Unfortunately libdwfl doesn't
allow a single unwind and then an update to the return address to be 0
based as there are assertions that registers aren't updated once an
unwind has started, etc.

As removing the dwfl didn't prove possible, an alternative is to just
not discard the dwfl when the unwind ends. The dwfl is valid for a
process unless a dso is loaded at the same address as a previous
one. So keep the dwfl with the maps, invalidate it if a map is removed
(in case a new map replaces it) and recycle the dwfl in the unwinding
code. A wrinkly in the implementation of this is that the attached
thread argument is remembered by the dwfl and so it needs to be a
pointer to memory that also persists with the dwfl (struct
dwfl_ui_thread_info in the code).

Recording 10 seconds of system wide data with --call-graph=dwarf and
then processing with perf report shows a total runtime improvement
from 41.583s to 2.279s (an 18x speedup).

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Aditya Bodkhe <aditya.b1@linux.ibm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Chun-Tse Shao <ctshao@google.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Haibo Xu <haibo1.xu@intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Krzysztof Łopatowski <krzysztof.m.lopatowski@gmail.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergei Trofimovich <slyich@gmail.com>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
6b2658b3 2e919157

+111 -26
+34 -2
tools/perf/util/maps.c
··· 10 10 #include "thread.h" 11 11 #include "ui/ui.h" 12 12 #include "unwind.h" 13 + #include "unwind-libdw.h" 13 14 #include <internal/rc_check.h> 14 15 15 16 /* ··· 40 39 #ifdef HAVE_LIBUNWIND_SUPPORT 41 40 void *addr_space; 42 41 const struct unwind_libunwind_ops *unwind_libunwind_ops; 42 + #endif 43 + #ifdef HAVE_LIBDW_SUPPORT 44 + void *libdw_addr_space_dwfl; 43 45 #endif 44 46 refcount_t refcnt; 45 47 /** ··· 207 203 RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops; 208 204 } 209 205 #endif 206 + #ifdef HAVE_LIBDW_SUPPORT 207 + void *maps__libdw_addr_space_dwfl(const struct maps *maps) 208 + { 209 + return RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl; 210 + } 211 + 212 + void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl) 213 + { 214 + RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl = dwfl; 215 + } 216 + #endif 210 217 211 218 static struct rw_semaphore *maps__lock(struct maps *maps) 212 219 { ··· 233 218 #ifdef HAVE_LIBUNWIND_SUPPORT 234 219 RC_CHK_ACCESS(maps)->addr_space = NULL; 235 220 RC_CHK_ACCESS(maps)->unwind_libunwind_ops = NULL; 221 + #endif 222 + #ifdef HAVE_LIBDW_SUPPORT 223 + RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl = NULL; 236 224 #endif 237 225 refcount_set(maps__refcnt(maps), 1); 238 226 RC_CHK_ACCESS(maps)->nr_maps = 0; ··· 258 240 zfree(&maps_by_address); 259 241 zfree(&maps_by_name); 260 242 unwind__finish_access(maps); 243 + #ifdef HAVE_LIBDW_SUPPORT 244 + libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); 245 + #endif 261 246 } 262 247 263 248 struct maps *maps__new(struct machine *machine) ··· 570 549 __maps__remove(maps, map); 571 550 check_invariants(maps); 572 551 up_write(maps__lock(maps)); 552 + #ifdef HAVE_LIBDW_SUPPORT 553 + libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); 554 + #endif 573 555 } 574 556 575 557 bool maps__empty(struct maps *maps) ··· 628 604 void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data) 629 605 { 630 606 struct map **maps_by_address; 607 + bool removed = false; 631 608 632 609 down_write(maps__lock(maps)); 633 610 634 611 maps_by_address = maps__maps_by_address(maps); 635 612 for (unsigned int i = 0; i < maps__nr_maps(maps);) { 636 - if (cb(maps_by_address[i], data)) 613 + if (cb(maps_by_address[i], data)) { 637 614 __maps__remove(maps, maps_by_address[i]); 638 - else 615 + removed = true; 616 + } else { 639 617 i++; 618 + } 640 619 } 641 620 check_invariants(maps); 642 621 up_write(maps__lock(maps)); 622 + if (removed) { 623 + #ifdef HAVE_LIBDW_SUPPORT 624 + libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); 625 + #endif 626 + } 643 627 } 644 628 645 629 struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp)
+4
tools/perf/util/maps.h
··· 52 52 const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps); 53 53 void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops); 54 54 #endif 55 + #ifdef HAVE_LIBDW_SUPPORT 56 + void *maps__libdw_addr_space_dwfl(const struct maps *maps); 57 + void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl); 58 + #endif 55 59 56 60 size_t maps__fprintf(struct maps *maps, FILE *fp); 57 61
+66 -22
tools/perf/util/unwind-libdw.c
··· 20 20 #include "callchain.h" 21 21 #include "util/env.h" 22 22 23 + /* 24 + * The dwfl thread argument passed to functions like memory_read. Memory has to 25 + * be allocated to persist of multiple uses of the dwfl. 26 + */ 27 + struct dwfl_ui_thread_info { 28 + /* Back link to the dwfl. */ 29 + Dwfl *dwfl; 30 + /* The current unwind info, only 1 is supported. */ 31 + struct unwind_info *ui; 32 + }; 33 + 23 34 static char *debuginfo_path; 24 35 25 36 static int __find_debuginfo(Dwfl_Module *mod __maybe_unused, void **userdata, ··· 44 33 if (dso__symsrc_filename(dso) && strcmp(file_name, dso__symsrc_filename(dso))) 45 34 *debuginfo_file_name = strdup(dso__symsrc_filename(dso)); 46 35 return -1; 36 + } 37 + 38 + void libdw__invalidate_dwfl(struct maps *maps, void *arg) 39 + { 40 + struct dwfl_ui_thread_info *dwfl_ui_ti = arg; 41 + 42 + if (!dwfl_ui_ti) 43 + return; 44 + 45 + assert(dwfl_ui_ti->ui == NULL); 46 + maps__set_libdw_addr_space_dwfl(maps, NULL); 47 + dwfl_end(dwfl_ui_ti->dwfl); 48 + free(dwfl_ui_ti); 47 49 } 48 50 49 51 static const Dwfl_Callbacks offline_callbacks = { ··· 211 187 static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result, 212 188 void *arg) 213 189 { 214 - struct unwind_info *ui = arg; 190 + struct dwfl_ui_thread_info *dwfl_ui_ti = arg; 191 + struct unwind_info *ui = dwfl_ui_ti->ui; 215 192 uint16_t e_machine = thread__e_machine(ui->thread, ui->machine); 216 193 struct stack_dump *stack = &ui->sample->user_stack; 217 194 u64 start, end; ··· 253 228 254 229 static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg) 255 230 { 256 - struct unwind_info *ui = arg; 231 + struct dwfl_ui_thread_info *dwfl_ui_ti = arg; 232 + struct unwind_info *ui = dwfl_ui_ti->ui; 257 233 struct regs_dump *user_regs = perf_sample__user_regs(ui->sample); 258 234 Dwarf_Word *dwarf_regs; 259 235 int max_dwarf_reg = 0; ··· 346 320 int max_stack, 347 321 bool best_effort) 348 322 { 349 - struct machine *machine = maps__machine(thread__maps(thread)); 323 + struct maps *maps = thread__maps(thread); 324 + struct machine *machine = maps__machine(maps); 350 325 uint16_t e_machine = thread__e_machine(thread, machine); 351 - struct unwind_info *ui, ui_buf = { 326 + struct dwfl_ui_thread_info *dwfl_ui_ti; 327 + static struct unwind_info *ui; 328 + Dwfl *dwfl; 329 + Dwarf_Word ip; 330 + int err = -EINVAL, i; 331 + 332 + if (!data->user_regs || !data->user_regs->regs) 333 + return -EINVAL; 334 + 335 + ui = zalloc(sizeof(*ui) + sizeof(ui->entries[0]) * max_stack); 336 + if (!ui) 337 + return -ENOMEM; 338 + 339 + *ui = (struct unwind_info){ 352 340 .sample = data, 353 341 .thread = thread, 354 342 .machine = machine, ··· 372 332 .e_machine = e_machine, 373 333 .best_effort = best_effort 374 334 }; 375 - Dwarf_Word ip; 376 - int err = -EINVAL, i; 377 335 378 - if (!data->user_regs || !data->user_regs->regs) 379 - return -EINVAL; 336 + dwfl_ui_ti = maps__libdw_addr_space_dwfl(maps); 337 + if (dwfl_ui_ti) { 338 + dwfl = dwfl_ui_ti->dwfl; 339 + } else { 340 + dwfl_ui_ti = zalloc(sizeof(*dwfl_ui_ti)); 341 + dwfl = dwfl_begin(&offline_callbacks); 342 + if (!dwfl) 343 + goto out; 380 344 381 - ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack); 382 - if (!ui) 383 - return -ENOMEM; 384 - 385 - *ui = ui_buf; 386 - 387 - ui->dwfl = dwfl_begin(&offline_callbacks); 388 - if (!ui->dwfl) 389 - goto out; 345 + dwfl_ui_ti->dwfl = dwfl; 346 + maps__set_libdw_addr_space_dwfl(maps, dwfl_ui_ti); 347 + } 348 + assert(dwfl_ui_ti->ui == NULL); 349 + assert(dwfl_ui_ti->dwfl == dwfl); 350 + assert(dwfl_ui_ti == maps__libdw_addr_space_dwfl(maps)); 351 + dwfl_ui_ti->ui = ui; 352 + ui->dwfl = dwfl; 390 353 391 354 err = perf_reg_value(&ip, data->user_regs, perf_arch_reg_ip(e_machine)); 392 355 if (err) ··· 399 356 if (err) 400 357 goto out; 401 358 402 - err = !dwfl_attach_state(ui->dwfl, /*elf=*/NULL, thread__tid(thread), &callbacks, ui); 403 - if (err) 404 - goto out; 359 + dwfl_attach_state(dwfl, /*elf=*/NULL, thread__tid(thread), &callbacks, 360 + /* Dwfl thread function argument*/dwfl_ui_ti); 361 + // Ignore thread already attached error. 405 362 406 - err = dwfl_getthread_frames(ui->dwfl, thread__tid(thread), frame_callback, ui); 363 + err = dwfl_getthread_frames(dwfl, thread__tid(thread), frame_callback, 364 + /* Dwfl frame function argument*/ui); 407 365 408 366 if (err && ui->max_stack != max_stack) 409 367 err = 0; ··· 428 384 for (i = 0; i < ui->idx; i++) 429 385 map_symbol__exit(&ui->entries[i].ms); 430 386 431 - dwfl_end(ui->dwfl); 387 + dwfl_ui_ti->ui = NULL; 432 388 free(ui); 433 389 return 0; 434 390 }
+7 -2
tools/perf/util/unwind-libdw.h
··· 2 2 #ifndef __PERF_UNWIND_LIBDW_H 3 3 #define __PERF_UNWIND_LIBDW_H 4 4 5 - #include <elfutils/libdwfl.h> 5 + #include <stdint.h> 6 6 #include "unwind.h" 7 7 8 8 struct machine; 9 9 struct perf_sample; 10 10 struct thread; 11 11 12 + #ifdef HAVE_LIBDW_SUPPORT 13 + 12 14 struct unwind_info { 13 - Dwfl *dwfl; 15 + void *dwfl; 14 16 struct perf_sample *sample; 15 17 struct machine *machine; 16 18 struct thread *thread; ··· 24 22 bool best_effort; 25 23 struct unwind_entry entries[]; 26 24 }; 25 + 26 + void libdw__invalidate_dwfl(struct maps *maps, void *dwfl); 27 + #endif 27 28 28 29 #endif /* __PERF_UNWIND_LIBDW_H */