Skip to content

Commit 6b2658b

Browse files
captain5050acmel
authored andcommitted
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 <[email protected]> Cc: Aditya Bodkhe <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Albert Ou <[email protected]> Cc: Alexandre Ghiti <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Athira Rajeev <[email protected]> Cc: Chun-Tse Shao <[email protected]> Cc: Dmitriy Vyukov <[email protected]> Cc: Dr. David Alan Gilbert <[email protected]> Cc: Guo Ren <[email protected]> Cc: Haibo Xu <[email protected]> Cc: Howard Chu <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: James Clark <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: John Garry <[email protected]> Cc: Krzysztof Łopatowski <[email protected]> Cc: Leo Yan <[email protected]> Cc: Mark Wielaard <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Palmer Dabbelt <[email protected]> Cc: Paul Walmsley <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Sergei Trofimovich <[email protected]> Cc: Shimin Guo <[email protected]> Cc: Stephen Brennan <[email protected]> Cc: Thomas Falcon <[email protected]> Cc: Will Deacon <[email protected]> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 2e91915 commit 6b2658b

4 files changed

Lines changed: 112 additions & 27 deletions

File tree

tools/perf/util/maps.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "thread.h"
1111
#include "ui/ui.h"
1212
#include "unwind.h"
13+
#include "unwind-libdw.h"
1314
#include <internal/rc_check.h>
1415

1516
/*
@@ -39,6 +40,9 @@ DECLARE_RC_STRUCT(maps) {
3940
#ifdef HAVE_LIBUNWIND_SUPPORT
4041
void *addr_space;
4142
const struct unwind_libunwind_ops *unwind_libunwind_ops;
43+
#endif
44+
#ifdef HAVE_LIBDW_SUPPORT
45+
void *libdw_addr_space_dwfl;
4246
#endif
4347
refcount_t refcnt;
4448
/**
@@ -203,6 +207,17 @@ void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libun
203207
RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
204208
}
205209
#endif
210+
#ifdef HAVE_LIBDW_SUPPORT
211+
void *maps__libdw_addr_space_dwfl(const struct maps *maps)
212+
{
213+
return RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl;
214+
}
215+
216+
void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl)
217+
{
218+
RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl = dwfl;
219+
}
220+
#endif
206221

207222
static struct rw_semaphore *maps__lock(struct maps *maps)
208223
{
@@ -218,6 +233,9 @@ static void maps__init(struct maps *maps, struct machine *machine)
218233
#ifdef HAVE_LIBUNWIND_SUPPORT
219234
RC_CHK_ACCESS(maps)->addr_space = NULL;
220235
RC_CHK_ACCESS(maps)->unwind_libunwind_ops = NULL;
236+
#endif
237+
#ifdef HAVE_LIBDW_SUPPORT
238+
RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl = NULL;
221239
#endif
222240
refcount_set(maps__refcnt(maps), 1);
223241
RC_CHK_ACCESS(maps)->nr_maps = 0;
@@ -240,6 +258,9 @@ static void maps__exit(struct maps *maps)
240258
zfree(&maps_by_address);
241259
zfree(&maps_by_name);
242260
unwind__finish_access(maps);
261+
#ifdef HAVE_LIBDW_SUPPORT
262+
libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps));
263+
#endif
243264
}
244265

245266
struct maps *maps__new(struct machine *machine)
@@ -549,6 +570,9 @@ void maps__remove(struct maps *maps, struct map *map)
549570
__maps__remove(maps, map);
550571
check_invariants(maps);
551572
up_write(maps__lock(maps));
573+
#ifdef HAVE_LIBDW_SUPPORT
574+
libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps));
575+
#endif
552576
}
553577

554578
bool maps__empty(struct maps *maps)
@@ -604,18 +628,26 @@ int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data)
604628
void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data)
605629
{
606630
struct map **maps_by_address;
631+
bool removed = false;
607632

608633
down_write(maps__lock(maps));
609634

610635
maps_by_address = maps__maps_by_address(maps);
611636
for (unsigned int i = 0; i < maps__nr_maps(maps);) {
612-
if (cb(maps_by_address[i], data))
637+
if (cb(maps_by_address[i], data)) {
613638
__maps__remove(maps, maps_by_address[i]);
614-
else
639+
removed = true;
640+
} else {
615641
i++;
642+
}
616643
}
617644
check_invariants(maps);
618645
up_write(maps__lock(maps));
646+
if (removed) {
647+
#ifdef HAVE_LIBDW_SUPPORT
648+
libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps));
649+
#endif
650+
}
619651
}
620652

621653
struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp)

tools/perf/util/maps.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ void maps__set_addr_space(struct maps *maps, void *addr_space);
5252
const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps);
5353
void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops);
5454
#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
5559

5660
size_t maps__fprintf(struct maps *maps, FILE *fp);
5761

tools/perf/util/unwind-libdw.c

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@
2020
#include "callchain.h"
2121
#include "util/env.h"
2222

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+
2334
static char *debuginfo_path;
2435

2536
static int __find_debuginfo(Dwfl_Module *mod __maybe_unused, void **userdata,
@@ -35,6 +46,19 @@ static int __find_debuginfo(Dwfl_Module *mod __maybe_unused, void **userdata,
3546
return -1;
3647
}
3748

49+
void libdw__invalidate_dwfl(struct maps *maps, void *arg)
50+
{
51+
struct dwfl_ui_thread_info *dwfl_ui_ti = arg;
52+
53+
if (!dwfl_ui_ti)
54+
return;
55+
56+
assert(dwfl_ui_ti->ui == NULL);
57+
maps__set_libdw_addr_space_dwfl(maps, NULL);
58+
dwfl_end(dwfl_ui_ti->dwfl);
59+
free(dwfl_ui_ti);
60+
}
61+
3862
static const Dwfl_Callbacks offline_callbacks = {
3963
.find_debuginfo = __find_debuginfo,
4064
.debuginfo_path = &debuginfo_path,
@@ -187,7 +211,8 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
187211
static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result,
188212
void *arg)
189213
{
190-
struct unwind_info *ui = arg;
214+
struct dwfl_ui_thread_info *dwfl_ui_ti = arg;
215+
struct unwind_info *ui = dwfl_ui_ti->ui;
191216
uint16_t e_machine = thread__e_machine(ui->thread, ui->machine);
192217
struct stack_dump *stack = &ui->sample->user_stack;
193218
u64 start, end;
@@ -228,7 +253,8 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
228253

229254
static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg)
230255
{
231-
struct unwind_info *ui = arg;
256+
struct dwfl_ui_thread_info *dwfl_ui_ti = arg;
257+
struct unwind_info *ui = dwfl_ui_ti->ui;
232258
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
233259
Dwarf_Word *dwarf_regs;
234260
int max_dwarf_reg = 0;
@@ -320,33 +346,50 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
320346
int max_stack,
321347
bool best_effort)
322348
{
323-
struct machine *machine = maps__machine(thread__maps(thread));
349+
struct maps *maps = thread__maps(thread);
350+
struct machine *machine = maps__machine(maps);
324351
uint16_t e_machine = thread__e_machine(thread, machine);
325-
struct unwind_info *ui, ui_buf = {
326-
.sample = data,
327-
.thread = thread,
328-
.machine = machine,
329-
.cb = cb,
330-
.arg = arg,
331-
.max_stack = max_stack,
332-
.e_machine = e_machine,
333-
.best_effort = best_effort
334-
};
352+
struct dwfl_ui_thread_info *dwfl_ui_ti;
353+
static struct unwind_info *ui;
354+
Dwfl *dwfl;
335355
Dwarf_Word ip;
336356
int err = -EINVAL, i;
337357

338358
if (!data->user_regs || !data->user_regs->regs)
339359
return -EINVAL;
340360

341-
ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack);
361+
ui = zalloc(sizeof(*ui) + sizeof(ui->entries[0]) * max_stack);
342362
if (!ui)
343363
return -ENOMEM;
344364

345-
*ui = ui_buf;
365+
*ui = (struct unwind_info){
366+
.sample = data,
367+
.thread = thread,
368+
.machine = machine,
369+
.cb = cb,
370+
.arg = arg,
371+
.max_stack = max_stack,
372+
.e_machine = e_machine,
373+
.best_effort = best_effort
374+
};
346375

347-
ui->dwfl = dwfl_begin(&offline_callbacks);
348-
if (!ui->dwfl)
349-
goto out;
376+
dwfl_ui_ti = maps__libdw_addr_space_dwfl(maps);
377+
if (dwfl_ui_ti) {
378+
dwfl = dwfl_ui_ti->dwfl;
379+
} else {
380+
dwfl_ui_ti = zalloc(sizeof(*dwfl_ui_ti));
381+
dwfl = dwfl_begin(&offline_callbacks);
382+
if (!dwfl)
383+
goto out;
384+
385+
dwfl_ui_ti->dwfl = dwfl;
386+
maps__set_libdw_addr_space_dwfl(maps, dwfl_ui_ti);
387+
}
388+
assert(dwfl_ui_ti->ui == NULL);
389+
assert(dwfl_ui_ti->dwfl == dwfl);
390+
assert(dwfl_ui_ti == maps__libdw_addr_space_dwfl(maps));
391+
dwfl_ui_ti->ui = ui;
392+
ui->dwfl = dwfl;
350393

351394
err = perf_reg_value(&ip, data->user_regs, perf_arch_reg_ip(e_machine));
352395
if (err)
@@ -356,11 +399,12 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
356399
if (err)
357400
goto out;
358401

359-
err = !dwfl_attach_state(ui->dwfl, /*elf=*/NULL, thread__tid(thread), &callbacks, ui);
360-
if (err)
361-
goto out;
402+
dwfl_attach_state(dwfl, /*elf=*/NULL, thread__tid(thread), &callbacks,
403+
/* Dwfl thread function argument*/dwfl_ui_ti);
404+
// Ignore thread already attached error.
362405

363-
err = dwfl_getthread_frames(ui->dwfl, thread__tid(thread), frame_callback, ui);
406+
err = dwfl_getthread_frames(dwfl, thread__tid(thread), frame_callback,
407+
/* Dwfl frame function argument*/ui);
364408

365409
if (err && ui->max_stack != max_stack)
366410
err = 0;
@@ -384,7 +428,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
384428
for (i = 0; i < ui->idx; i++)
385429
map_symbol__exit(&ui->entries[i].ms);
386430

387-
dwfl_end(ui->dwfl);
431+
dwfl_ui_ti->ui = NULL;
388432
free(ui);
389433
return 0;
390434
}

tools/perf/util/unwind-libdw.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
#ifndef __PERF_UNWIND_LIBDW_H
33
#define __PERF_UNWIND_LIBDW_H
44

5-
#include <elfutils/libdwfl.h>
5+
#include <stdint.h>
66
#include "unwind.h"
77

88
struct machine;
99
struct perf_sample;
1010
struct thread;
1111

12+
#ifdef HAVE_LIBDW_SUPPORT
13+
1214
struct unwind_info {
13-
Dwfl *dwfl;
15+
void *dwfl;
1416
struct perf_sample *sample;
1517
struct machine *machine;
1618
struct thread *thread;
@@ -23,4 +25,7 @@ struct unwind_info {
2325
struct unwind_entry entries[];
2426
};
2527

28+
void libdw__invalidate_dwfl(struct maps *maps, void *dwfl);
29+
#endif
30+
2631
#endif /* __PERF_UNWIND_LIBDW_H */

0 commit comments

Comments
 (0)