1 /*
2 * Copyright (c) 2023 KNS Group LLC (YADRO)
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8
valid_stack(uintptr_t addr,k_tid_t current)9 static bool valid_stack(uintptr_t addr, k_tid_t current)
10 {
11 return current->stack_info.start <= addr &&
12 addr < current->stack_info.start + current->stack_info.size;
13 }
14
in_text_region(uintptr_t addr)15 static inline bool in_text_region(uintptr_t addr)
16 {
17 extern uintptr_t __text_region_start, __text_region_end;
18
19 return (addr >= (uintptr_t)&__text_region_start) && (addr < (uintptr_t)&__text_region_end);
20 }
21
22 /*
23 * This function use frame pointers to unwind stack and get trace of return addresses.
24 * Return addresses are translated in corresponding function's names using .elf file.
25 * So we get function call trace
26 */
arch_perf_current_stack_trace(uintptr_t * buf,size_t size)27 size_t arch_perf_current_stack_trace(uintptr_t *buf, size_t size)
28 {
29 if (size < 2U) {
30 return 0;
31 }
32
33 size_t idx = 0;
34
35 /*
36 * In riscv (arch/riscv/core/isr.S) ra, ip($mepc) and fp($s0) are saved
37 * at the beginning of _isr_wrapper in order, specified by z_arch_esf_t.
38 * Then, before calling interruption handler, core switch $sp to
39 * _current_cpu->irq_stack and save $sp with offset -16 on irq stack
40 *
41 * The following lines lines do the reverse things to get ra, ip anf fp
42 * from thread stack
43 */
44 const struct arch_esf * const esf =
45 *((struct arch_esf **)(((uintptr_t)_current_cpu->irq_stack) - 16));
46
47 /*
48 * $s0 is used as frame pointer.
49 *
50 * stack frame in memory (commonly):
51 * (addresses growth up)
52 * ....
53 * [-] <- $fp($s0) (curr)
54 * $ra
55 * $fp($s0) (next)
56 * ....
57 *
58 * If function do not call any other function, compiller may not save $ra,
59 * then stack frame will be:
60 * ....
61 * [-] <- $fp($s0) (curr)
62 * $fp($s0) (next)
63 * ....
64 *
65 */
66 void **fp = (void **)esf->s0;
67 void **new_fp = (void **)fp[-1];
68
69 buf[idx++] = (uintptr_t)esf->mepc;
70
71 /*
72 * During function prologue and epilogue fp is equal to fp of
73 * previous function stack frame, it looks like second function
74 * from top is missed.
75 * So saving $ra will help in case when irq occurred in
76 * function prologue or epilogue.
77 */
78 buf[idx++] = (uintptr_t)esf->ra;
79 if (valid_stack((uintptr_t)new_fp, arch_current_thread())) {
80 fp = new_fp;
81 }
82 while (valid_stack((uintptr_t)fp, arch_current_thread())) {
83 if (idx >= size) {
84 return 0;
85 }
86
87 if (!in_text_region((uintptr_t)fp[-1])) {
88 break;
89 }
90
91 buf[idx++] = (uintptr_t)fp[-1];
92 new_fp = (void **)fp[-2];
93
94 /*
95 * anti-infinity-loop if
96 * new_fp can't be smaller than fp, cause the stack is growing down
97 * and trace moves deeper into the stack
98 */
99 if (new_fp <= fp) {
100 break;
101 }
102 fp = new_fp;
103 }
104
105 return idx;
106 }
107