1b9f6fbb3SAlexandre Truong // SPDX-License-Identifier: GPL-2.0
2b9f6fbb3SAlexandre Truong #include "arm64-frame-pointer-unwind-support.h"
3b9f6fbb3SAlexandre Truong #include "callchain.h"
4b9f6fbb3SAlexandre Truong #include "event.h"
5b9f6fbb3SAlexandre Truong #include "perf_regs.h" // SMPL_REG_MASK
6b9f6fbb3SAlexandre Truong #include "unwind.h"
7b9f6fbb3SAlexandre Truong
8b9f6fbb3SAlexandre Truong #define perf_event_arm_regs perf_event_arm64_regs
946f57d24SIan Rogers #include "../../arch/arm64/include/uapi/asm/perf_regs.h"
10b9f6fbb3SAlexandre Truong #undef perf_event_arm_regs
11b9f6fbb3SAlexandre Truong
12b9f6fbb3SAlexandre Truong struct entries {
13b9f6fbb3SAlexandre Truong u64 stack[2];
14b9f6fbb3SAlexandre Truong size_t length;
15b9f6fbb3SAlexandre Truong };
16b9f6fbb3SAlexandre Truong
get_leaf_frame_caller_enabled(struct perf_sample * sample)17b9f6fbb3SAlexandre Truong static bool get_leaf_frame_caller_enabled(struct perf_sample *sample)
18b9f6fbb3SAlexandre Truong {
19b9f6fbb3SAlexandre Truong return callchain_param.record_mode == CALLCHAIN_FP && sample->user_regs.regs
20b9f6fbb3SAlexandre Truong && sample->user_regs.mask & SMPL_REG_MASK(PERF_REG_ARM64_LR);
21b9f6fbb3SAlexandre Truong }
22b9f6fbb3SAlexandre Truong
add_entry(struct unwind_entry * entry,void * arg)23b9f6fbb3SAlexandre Truong static int add_entry(struct unwind_entry *entry, void *arg)
24b9f6fbb3SAlexandre Truong {
25b9f6fbb3SAlexandre Truong struct entries *entries = arg;
26b9f6fbb3SAlexandre Truong
27b9f6fbb3SAlexandre Truong entries->stack[entries->length++] = entry->ip;
28b9f6fbb3SAlexandre Truong return 0;
29b9f6fbb3SAlexandre Truong }
30b9f6fbb3SAlexandre Truong
get_leaf_frame_caller_aarch64(struct perf_sample * sample,struct thread * thread,int usr_idx)31b9f6fbb3SAlexandre Truong u64 get_leaf_frame_caller_aarch64(struct perf_sample *sample, struct thread *thread, int usr_idx)
32b9f6fbb3SAlexandre Truong {
33b9f6fbb3SAlexandre Truong int ret;
34b9f6fbb3SAlexandre Truong struct entries entries = {};
35b9f6fbb3SAlexandre Truong struct regs_dump old_regs = sample->user_regs;
36b9f6fbb3SAlexandre Truong
37b9f6fbb3SAlexandre Truong if (!get_leaf_frame_caller_enabled(sample))
38b9f6fbb3SAlexandre Truong return 0;
39b9f6fbb3SAlexandre Truong
40b9f6fbb3SAlexandre Truong /*
41b9f6fbb3SAlexandre Truong * If PC and SP are not recorded, get the value of PC from the stack
42b9f6fbb3SAlexandre Truong * and set its mask. SP is not used when doing the unwinding but it
43b9f6fbb3SAlexandre Truong * still needs to be set to prevent failures.
44b9f6fbb3SAlexandre Truong */
45b9f6fbb3SAlexandre Truong
46b9f6fbb3SAlexandre Truong if (!(sample->user_regs.mask & SMPL_REG_MASK(PERF_REG_ARM64_PC))) {
47b9f6fbb3SAlexandre Truong sample->user_regs.cache_mask |= SMPL_REG_MASK(PERF_REG_ARM64_PC);
48b9f6fbb3SAlexandre Truong sample->user_regs.cache_regs[PERF_REG_ARM64_PC] = sample->callchain->ips[usr_idx+1];
49b9f6fbb3SAlexandre Truong }
50b9f6fbb3SAlexandre Truong
51b9f6fbb3SAlexandre Truong if (!(sample->user_regs.mask & SMPL_REG_MASK(PERF_REG_ARM64_SP))) {
52b9f6fbb3SAlexandre Truong sample->user_regs.cache_mask |= SMPL_REG_MASK(PERF_REG_ARM64_SP);
53b9f6fbb3SAlexandre Truong sample->user_regs.cache_regs[PERF_REG_ARM64_SP] = 0;
54b9f6fbb3SAlexandre Truong }
55b9f6fbb3SAlexandre Truong
56*fa7095c5SJames Clark ret = unwind__get_entries(add_entry, &entries, thread, sample, 2, true);
57b9f6fbb3SAlexandre Truong sample->user_regs = old_regs;
58b9f6fbb3SAlexandre Truong
59b9f6fbb3SAlexandre Truong if (ret || entries.length != 2)
60b9f6fbb3SAlexandre Truong return ret;
61b9f6fbb3SAlexandre Truong
62b9f6fbb3SAlexandre Truong return callchain_param.order == ORDER_CALLER ? entries.stack[0] : entries.stack[1];
63b9f6fbb3SAlexandre Truong }
64