mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-19 18:53:52 +08:00
perf unwind: Don't show unwind error messages when augmenting frame pointer stack
Commit Fixes:b9f6fbb3b2
("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:b9f6fbb3b2
("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>
This commit is contained in:
parent
278aaba2c5
commit
fa7095c5c3
@ -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
|
|||||||
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,
|
|||||||
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,7 +200,8 @@ 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)) {
|
||||||
pr_err("%s", dwfl_errmsg(-1));
|
if (!ui->best_effort)
|
||||||
|
pr_err("%s", dwfl_errmsg(-1));
|
||||||
return DWARF_CB_ABORT;
|
return DWARF_CB_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,7 +209,8 @@ 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)) {
|
||||||
pr_err("%s", dwfl_errmsg(-1));
|
if (!ui->best_effort)
|
||||||
|
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)
|
|||||||
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,
|
|||||||
.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 {
|
|||||||
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 {
|
|||||||
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,7 +554,8 @@ 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) {
|
||||||
pr_err("unwind: can't read reg %d\n", regnum);
|
if (!ui->best_effort)
|
||||||
|
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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 {
|
|||||||
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,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user