1 /*
2  *  Copyright (c) 2023 KNS Group LLC (YADRO)
3  *  Copyright (c) 2020 Yonatan Goldschmidt <yon.goldschmidt@gmail.com>
4  *
5  *  SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/kernel.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 	extern uintptr_t __text_region_start, __text_region_end;
19 
20 	return (addr >= (uintptr_t)&__text_region_start) && (addr < (uintptr_t)&__text_region_end);
21 }
22 
23 /* interruption stack frame */
24 struct isf {
25 	uint32_t ebp;
26 	uint32_t ecx;
27 	uint32_t edx;
28 	uint32_t eax;
29 	uint32_t eip;
30 };
31 
32 /*
33  * This function use frame pointers to unwind stack and get trace of return addresses.
34  * Return addresses are translated in corresponding function's names using .elf file.
35  * So we get function call trace
36  */
arch_perf_current_stack_trace(uintptr_t * buf,size_t size)37 size_t arch_perf_current_stack_trace(uintptr_t *buf, size_t size)
38 {
39 	if (size < 1U) {
40 		return 0;
41 	}
42 
43 	size_t idx = 0;
44 
45 	const struct isf * const isf =
46 		*((struct isf **)(((void **)_current_cpu->irq_stack)-1));
47 	/*
48 	 * In x86 (arch/x86/core/ia32/intstub.S) %eip and %ebp
49 	 * are saved at the beginning of _interrupt_enter in order, that described
50 	 * in struct esf. Core switch %esp to
51 	 * _current_cpu->irq_stack and push %esp on irq stack
52 	 *
53 	 * The following lines lines do the reverse things to get %eip and %ebp
54 	 * from thread stack
55 	 */
56 	void **fp = (void **)isf->ebp;
57 
58 	/*
59 	 * %ebp is frame pointer.
60 	 *
61 	 * stack frame in memory:
62 	 * (addresses growth up)
63 	 *  ....
64 	 *  ra
65 	 *  %ebp (next) <- %ebp (curr)
66 	 *  ....
67 	 */
68 
69 	buf[idx++] = (uintptr_t)isf->eip;
70 	while (valid_stack((uintptr_t)fp, arch_current_thread())) {
71 		if (idx >= size) {
72 			return 0;
73 		}
74 
75 		if (!in_text_region((uintptr_t)fp[1])) {
76 			break;
77 		}
78 
79 		buf[idx++] = (uintptr_t)fp[1];
80 		void **new_fp = (void **)fp[0];
81 
82 		/*
83 		 * anti-infinity-loop if
84 		 * new_fp can't be smaller than fp, cause the stack is growing down
85 		 * and trace moves deeper into the stack
86 		 */
87 		if (new_fp <= fp) {
88 			break;
89 		}
90 		fp = new_fp;
91 	}
92 
93 	return idx;
94 }
95