Commit fa7095c5 authored by James Clark's avatar James Clark Committed by Arnaldo Carvalho de Melo

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

Commit Fixes: b9f6fbb3 ("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: b9f6fbb3 ("perf arm64: Inject missing frames when using 'perf record --call-graph=fp'")
Reported-by: default avatarJohn Garry <john.garry@huawei.com>
Signed-off-by: default avatarJames Clark <james.clark@arm.com>
Tested-by: default avatarJohn 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 278aaba2
...@@ -122,7 +122,7 @@ NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__thread(struct thread *thr ...@@ -122,7 +122,7 @@ NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__thread(struct thread *thr
} }
err = unwind__get_entries(unwind_entry, &cnt, thread, err = unwind__get_entries(unwind_entry, &cnt, thread,
&sample, MAX_STACK); &sample, MAX_STACK, false);
if (err) if (err)
pr_debug("unwind failed\n"); pr_debug("unwind failed\n");
else if (cnt != MAX_STACK) { else if (cnt != MAX_STACK) {
......
...@@ -53,7 +53,7 @@ u64 get_leaf_frame_caller_aarch64(struct perf_sample *sample, struct thread *thr ...@@ -53,7 +53,7 @@ u64 get_leaf_frame_caller_aarch64(struct perf_sample *sample, struct thread *thr
sample->user_regs.cache_regs[PERF_REG_ARM64_SP] = 0; sample->user_regs.cache_regs[PERF_REG_ARM64_SP] = 0;
} }
ret = unwind__get_entries(add_entry, &entries, thread, sample, 2); ret = unwind__get_entries(add_entry, &entries, thread, sample, 2, true);
sample->user_regs = old_regs; sample->user_regs = old_regs;
if (ret || entries.length != 2) if (ret || entries.length != 2)
......
...@@ -2987,7 +2987,7 @@ static int thread__resolve_callchain_unwind(struct thread *thread, ...@@ -2987,7 +2987,7 @@ static int thread__resolve_callchain_unwind(struct thread *thread,
return 0; return 0;
return unwind__get_entries(unwind_entry, cursor, return unwind__get_entries(unwind_entry, cursor,
thread, sample, max_stack); thread, sample, max_stack, false);
} }
int thread__resolve_callchain(struct thread *thread, int thread__resolve_callchain(struct thread *thread,
......
...@@ -200,6 +200,7 @@ frame_callback(Dwfl_Frame *state, void *arg) ...@@ -200,6 +200,7 @@ frame_callback(Dwfl_Frame *state, void *arg)
bool isactivation; bool isactivation;
if (!dwfl_frame_pc(state, &pc, NULL)) { if (!dwfl_frame_pc(state, &pc, NULL)) {
if (!ui->best_effort)
pr_err("%s", dwfl_errmsg(-1)); pr_err("%s", dwfl_errmsg(-1));
return DWARF_CB_ABORT; return DWARF_CB_ABORT;
} }
...@@ -208,6 +209,7 @@ frame_callback(Dwfl_Frame *state, void *arg) ...@@ -208,6 +209,7 @@ frame_callback(Dwfl_Frame *state, void *arg)
report_module(pc, ui); report_module(pc, ui);
if (!dwfl_frame_pc(state, &pc, &isactivation)) { if (!dwfl_frame_pc(state, &pc, &isactivation)) {
if (!ui->best_effort)
pr_err("%s", dwfl_errmsg(-1)); pr_err("%s", dwfl_errmsg(-1));
return DWARF_CB_ABORT; return DWARF_CB_ABORT;
} }
...@@ -222,7 +224,8 @@ frame_callback(Dwfl_Frame *state, void *arg) ...@@ -222,7 +224,8 @@ frame_callback(Dwfl_Frame *state, void *arg)
int unwind__get_entries(unwind_entry_cb_t cb, void *arg, int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread, struct thread *thread,
struct perf_sample *data, struct perf_sample *data,
int max_stack) int max_stack,
bool best_effort)
{ {
struct unwind_info *ui, ui_buf = { struct unwind_info *ui, ui_buf = {
.sample = data, .sample = data,
...@@ -231,6 +234,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, ...@@ -231,6 +234,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
.cb = cb, .cb = cb,
.arg = arg, .arg = arg,
.max_stack = max_stack, .max_stack = max_stack,
.best_effort = best_effort
}; };
Dwarf_Word ip; Dwarf_Word ip;
int err = -EINVAL, i; int err = -EINVAL, i;
......
...@@ -20,6 +20,7 @@ struct unwind_info { ...@@ -20,6 +20,7 @@ struct unwind_info {
void *arg; void *arg;
int max_stack; int max_stack;
int idx; int idx;
bool best_effort;
struct unwind_entry entries[]; struct unwind_entry entries[];
}; };
......
...@@ -96,6 +96,7 @@ struct unwind_info { ...@@ -96,6 +96,7 @@ struct unwind_info {
struct perf_sample *sample; struct perf_sample *sample;
struct machine *machine; struct machine *machine;
struct thread *thread; struct thread *thread;
bool best_effort;
}; };
#define dw_read(ptr, type, end) ({ \ #define dw_read(ptr, type, end) ({ \
...@@ -553,6 +554,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as, ...@@ -553,6 +554,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as,
ret = perf_reg_value(&val, &ui->sample->user_regs, id); ret = perf_reg_value(&val, &ui->sample->user_regs, id);
if (ret) { if (ret) {
if (!ui->best_effort)
pr_err("unwind: can't read reg %d\n", regnum); pr_err("unwind: can't read reg %d\n", regnum);
return ret; return ret;
} }
...@@ -666,7 +668,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, ...@@ -666,7 +668,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
return -1; return -1;
ret = unw_init_remote(&c, addr_space, ui); ret = unw_init_remote(&c, addr_space, ui);
if (ret) if (ret && !ui->best_effort)
display_error(ret); display_error(ret);
while (!ret && (unw_step(&c) > 0) && i < max_stack) { while (!ret && (unw_step(&c) > 0) && i < max_stack) {
...@@ -704,12 +706,14 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, ...@@ -704,12 +706,14 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread, struct thread *thread,
struct perf_sample *data, int max_stack) struct perf_sample *data, int max_stack,
bool best_effort)
{ {
struct unwind_info ui = { struct unwind_info ui = {
.sample = data, .sample = data,
.thread = thread, .thread = thread,
.machine = thread->maps->machine, .machine = thread->maps->machine,
.best_effort = best_effort
}; };
if (!data->user_regs.regs) if (!data->user_regs.regs)
......
...@@ -80,9 +80,11 @@ void unwind__finish_access(struct maps *maps) ...@@ -80,9 +80,11 @@ void unwind__finish_access(struct maps *maps)
int unwind__get_entries(unwind_entry_cb_t cb, void *arg, int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread, struct thread *thread,
struct perf_sample *data, int max_stack) struct perf_sample *data, int max_stack,
bool best_effort)
{ {
if (thread->maps->unwind_libunwind_ops) if (thread->maps->unwind_libunwind_ops)
return thread->maps->unwind_libunwind_ops->get_entries(cb, arg, thread, data, max_stack); return thread->maps->unwind_libunwind_ops->get_entries(cb, arg, thread, data,
max_stack, best_effort);
return 0; return 0;
} }
...@@ -23,13 +23,19 @@ struct unwind_libunwind_ops { ...@@ -23,13 +23,19 @@ struct unwind_libunwind_ops {
void (*finish_access)(struct maps *maps); void (*finish_access)(struct maps *maps);
int (*get_entries)(unwind_entry_cb_t cb, void *arg, int (*get_entries)(unwind_entry_cb_t cb, void *arg,
struct thread *thread, struct thread *thread,
struct perf_sample *data, int max_stack); struct perf_sample *data, int max_stack, bool best_effort);
}; };
#ifdef HAVE_DWARF_UNWIND_SUPPORT #ifdef HAVE_DWARF_UNWIND_SUPPORT
/*
* When best_effort is set, don't report errors and fail silently. This could
* be expanded in the future to be more permissive about things other than
* error messages.
*/
int unwind__get_entries(unwind_entry_cb_t cb, void *arg, int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread, struct thread *thread,
struct perf_sample *data, int max_stack); struct perf_sample *data, int max_stack,
bool best_effort);
/* libunwind specific */ /* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT #ifdef HAVE_LIBUNWIND_SUPPORT
#ifndef LIBUNWIND__ARCH_REG_ID #ifndef LIBUNWIND__ARCH_REG_ID
...@@ -65,7 +71,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, ...@@ -65,7 +71,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
void *arg __maybe_unused, void *arg __maybe_unused,
struct thread *thread __maybe_unused, struct thread *thread __maybe_unused,
struct perf_sample *data __maybe_unused, struct perf_sample *data __maybe_unused,
int max_stack __maybe_unused) int max_stack __maybe_unused,
bool best_effort __maybe_unused)
{ {
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment