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

perf unwind: Don't show unwind error messages when augmenting frame pointer stack

Commit Fixes: b9f6fbb3b2c29736 ("perf arm64: Inject missing frames when
using 'perf record --call-graph=fp'") intended to add a 'best effort'
DWARF unwind that improved the frame pointer stack in most scenarios.

It's expected that the unwind will fail sometimes, but this shouldn't be
reported as an error. It only works when the return address can be
determined from the contents of the link register alone.

Fix the error shown when the unwinder requires extra registers by adding
a new flag that suppresses error messages. This flag is not set in the
normal --call-graph=dwarf unwind mode so that behavior is not changed.

Fixes: b9f6fbb3b2c29736 ("perf arm64: Inject missing frames when using 'perf record --call-graph=fp'")
Reported-by: John Garry <john.garry@huawei.com>
Signed-off-by: James Clark <james.clark@arm.com>
Tested-by: John Garry <john.garry@huawei.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Truong <alexandre.truong@arm.com>
Cc: German Gomez <german.gomez@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20220406145651.1392529-1-james.clark@arm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

James Clark and committed by
Arnaldo Carvalho de Melo
fa7095c5 278aaba2

+32 -14
+1 -1
tools/perf/tests/dwarf-unwind.c
··· 122 122 } 123 123 124 124 err = unwind__get_entries(unwind_entry, &cnt, thread, 125 - &sample, MAX_STACK); 125 + &sample, MAX_STACK, false); 126 126 if (err) 127 127 pr_debug("unwind failed\n"); 128 128 else if (cnt != MAX_STACK) {
+1 -1
tools/perf/util/arm64-frame-pointer-unwind-support.c
··· 53 53 sample->user_regs.cache_regs[PERF_REG_ARM64_SP] = 0; 54 54 } 55 55 56 - ret = unwind__get_entries(add_entry, &entries, thread, sample, 2); 56 + ret = unwind__get_entries(add_entry, &entries, thread, sample, 2, true); 57 57 sample->user_regs = old_regs; 58 58 59 59 if (ret || entries.length != 2)
+1 -1
tools/perf/util/machine.c
··· 2987 2987 return 0; 2988 2988 2989 2989 return unwind__get_entries(unwind_entry, cursor, 2990 - thread, sample, max_stack); 2990 + thread, sample, max_stack, false); 2991 2991 } 2992 2992 2993 2993 int thread__resolve_callchain(struct thread *thread,
+7 -3
tools/perf/util/unwind-libdw.c
··· 200 200 bool isactivation; 201 201 202 202 if (!dwfl_frame_pc(state, &pc, NULL)) { 203 - pr_err("%s", dwfl_errmsg(-1)); 203 + if (!ui->best_effort) 204 + pr_err("%s", dwfl_errmsg(-1)); 204 205 return DWARF_CB_ABORT; 205 206 } 206 207 ··· 209 208 report_module(pc, ui); 210 209 211 210 if (!dwfl_frame_pc(state, &pc, &isactivation)) { 212 - pr_err("%s", dwfl_errmsg(-1)); 211 + if (!ui->best_effort) 212 + pr_err("%s", dwfl_errmsg(-1)); 213 213 return DWARF_CB_ABORT; 214 214 } 215 215 ··· 224 222 int unwind__get_entries(unwind_entry_cb_t cb, void *arg, 225 223 struct thread *thread, 226 224 struct perf_sample *data, 227 - int max_stack) 225 + int max_stack, 226 + bool best_effort) 228 227 { 229 228 struct unwind_info *ui, ui_buf = { 230 229 .sample = data, ··· 234 231 .cb = cb, 235 232 .arg = arg, 236 233 .max_stack = max_stack, 234 + .best_effort = best_effort 237 235 }; 238 236 Dwarf_Word ip; 239 237 int err = -EINVAL, i;
+1
tools/perf/util/unwind-libdw.h
··· 20 20 void *arg; 21 21 int max_stack; 22 22 int idx; 23 + bool best_effort; 23 24 struct unwind_entry entries[]; 24 25 }; 25 26
+7 -3
tools/perf/util/unwind-libunwind-local.c
··· 96 96 struct perf_sample *sample; 97 97 struct machine *machine; 98 98 struct thread *thread; 99 + bool best_effort; 99 100 }; 100 101 101 102 #define dw_read(ptr, type, end) ({ \ ··· 554 553 555 554 ret = perf_reg_value(&val, &ui->sample->user_regs, id); 556 555 if (ret) { 557 - pr_err("unwind: can't read reg %d\n", regnum); 556 + if (!ui->best_effort) 557 + pr_err("unwind: can't read reg %d\n", regnum); 558 558 return ret; 559 559 } 560 560 ··· 668 666 return -1; 669 667 670 668 ret = unw_init_remote(&c, addr_space, ui); 671 - if (ret) 669 + if (ret && !ui->best_effort) 672 670 display_error(ret); 673 671 674 672 while (!ret && (unw_step(&c) > 0) && i < max_stack) { ··· 706 704 707 705 static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, 708 706 struct thread *thread, 709 - struct perf_sample *data, int max_stack) 707 + struct perf_sample *data, int max_stack, 708 + bool best_effort) 710 709 { 711 710 struct unwind_info ui = { 712 711 .sample = data, 713 712 .thread = thread, 714 713 .machine = thread->maps->machine, 714 + .best_effort = best_effort 715 715 }; 716 716 717 717 if (!data->user_regs.regs)
+4 -2
tools/perf/util/unwind-libunwind.c
··· 80 80 81 81 int unwind__get_entries(unwind_entry_cb_t cb, void *arg, 82 82 struct thread *thread, 83 - struct perf_sample *data, int max_stack) 83 + struct perf_sample *data, int max_stack, 84 + bool best_effort) 84 85 { 85 86 if (thread->maps->unwind_libunwind_ops) 86 - return thread->maps->unwind_libunwind_ops->get_entries(cb, arg, thread, data, max_stack); 87 + return thread->maps->unwind_libunwind_ops->get_entries(cb, arg, thread, data, 88 + max_stack, best_effort); 87 89 return 0; 88 90 }
+10 -3
tools/perf/util/unwind.h
··· 23 23 void (*finish_access)(struct maps *maps); 24 24 int (*get_entries)(unwind_entry_cb_t cb, void *arg, 25 25 struct thread *thread, 26 - struct perf_sample *data, int max_stack); 26 + struct perf_sample *data, int max_stack, bool best_effort); 27 27 }; 28 28 29 29 #ifdef HAVE_DWARF_UNWIND_SUPPORT 30 + /* 31 + * When best_effort is set, don't report errors and fail silently. This could 32 + * be expanded in the future to be more permissive about things other than 33 + * error messages. 34 + */ 30 35 int unwind__get_entries(unwind_entry_cb_t cb, void *arg, 31 36 struct thread *thread, 32 - struct perf_sample *data, int max_stack); 37 + struct perf_sample *data, int max_stack, 38 + bool best_effort); 33 39 /* libunwind specific */ 34 40 #ifdef HAVE_LIBUNWIND_SUPPORT 35 41 #ifndef LIBUNWIND__ARCH_REG_ID ··· 71 65 void *arg __maybe_unused, 72 66 struct thread *thread __maybe_unused, 73 67 struct perf_sample *data __maybe_unused, 74 - int max_stack __maybe_unused) 68 + int max_stack __maybe_unused, 69 + bool best_effort __maybe_unused) 75 70 { 76 71 return 0; 77 72 }