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