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