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