forked from Minki/linux
fa7095c5c3
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>
64 lines
1.8 KiB
C
64 lines
1.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include "arm64-frame-pointer-unwind-support.h"
|
|
#include "callchain.h"
|
|
#include "event.h"
|
|
#include "perf_regs.h" // SMPL_REG_MASK
|
|
#include "unwind.h"
|
|
|
|
#define perf_event_arm_regs perf_event_arm64_regs
|
|
#include "../../arch/arm64/include/uapi/asm/perf_regs.h"
|
|
#undef perf_event_arm_regs
|
|
|
|
struct entries {
|
|
u64 stack[2];
|
|
size_t length;
|
|
};
|
|
|
|
static bool get_leaf_frame_caller_enabled(struct perf_sample *sample)
|
|
{
|
|
return callchain_param.record_mode == CALLCHAIN_FP && sample->user_regs.regs
|
|
&& sample->user_regs.mask & SMPL_REG_MASK(PERF_REG_ARM64_LR);
|
|
}
|
|
|
|
static int add_entry(struct unwind_entry *entry, void *arg)
|
|
{
|
|
struct entries *entries = arg;
|
|
|
|
entries->stack[entries->length++] = entry->ip;
|
|
return 0;
|
|
}
|
|
|
|
u64 get_leaf_frame_caller_aarch64(struct perf_sample *sample, struct thread *thread, int usr_idx)
|
|
{
|
|
int ret;
|
|
struct entries entries = {};
|
|
struct regs_dump old_regs = sample->user_regs;
|
|
|
|
if (!get_leaf_frame_caller_enabled(sample))
|
|
return 0;
|
|
|
|
/*
|
|
* If PC and SP are not recorded, get the value of PC from the stack
|
|
* and set its mask. SP is not used when doing the unwinding but it
|
|
* still needs to be set to prevent failures.
|
|
*/
|
|
|
|
if (!(sample->user_regs.mask & SMPL_REG_MASK(PERF_REG_ARM64_PC))) {
|
|
sample->user_regs.cache_mask |= SMPL_REG_MASK(PERF_REG_ARM64_PC);
|
|
sample->user_regs.cache_regs[PERF_REG_ARM64_PC] = sample->callchain->ips[usr_idx+1];
|
|
}
|
|
|
|
if (!(sample->user_regs.mask & SMPL_REG_MASK(PERF_REG_ARM64_SP))) {
|
|
sample->user_regs.cache_mask |= SMPL_REG_MASK(PERF_REG_ARM64_SP);
|
|
sample->user_regs.cache_regs[PERF_REG_ARM64_SP] = 0;
|
|
}
|
|
|
|
ret = unwind__get_entries(add_entry, &entries, thread, sample, 2, true);
|
|
sample->user_regs = old_regs;
|
|
|
|
if (ret || entries.length != 2)
|
|
return ret;
|
|
|
|
return callchain_param.order == ORDER_CALLER ? entries.stack[0] : entries.stack[1];
|
|
}
|