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 /*
24  * This function use frame pointers to unwind stack and get trace of return addresses.
25  * Return addresses are translated in corresponding function's names using .elf file.
26  * So we get function call trace
27  */
arch_perf_current_stack_trace(uintptr_t * buf,size_t size)28 size_t arch_perf_current_stack_trace(uintptr_t *buf, size_t size)
29 {
30 	if (size < 1U) {
31 		return 0;
32 	}
33 
34 	size_t idx = 0;
35 
36 	/*
37 	 * In x86_64 (arch/x86/core/intel64/locore.S) %rip and %rbp
38 	 * are always saved in arch_current_thread()->callee_saved before calling
39 	 * handler function if interrupt is not nested
40 	 *
41 	 * %rip points the location where interrupt was occurred
42 	 */
43 	buf[idx++] = (uintptr_t)arch_current_thread()->callee_saved.rip;
44 	void **fp = (void **)arch_current_thread()->callee_saved.rbp;
45 
46 	/*
47 	 * %rbp is frame pointer.
48 	 *
49 	 * stack frame in memory:
50 	 * (addresses growth up)
51 	 *  ....
52 	 *  ra
53 	 *  %rbp (next) <- %rbp (curr)
54 	 *  ....
55 	 */
56 	while (valid_stack((uintptr_t)fp, arch_current_thread())) {
57 		if (idx >= size) {
58 			return 0;
59 		}
60 
61 		if (!in_text_region((uintptr_t)fp[1])) {
62 			break;
63 		}
64 
65 		buf[idx++] = (uintptr_t)fp[1];
66 		void **new_fp = (void **)fp[0];
67 
68 		/*
69 		 * anti-infinity-loop if
70 		 * new_fp can't be smaller than fp, cause the stack is growing down
71 		 * and trace moves deeper into the stack
72 		 */
73 		if (new_fp <= fp) {
74 			break;
75 		}
76 		fp = new_fp;
77 	}
78 
79 	return idx;
80 }
81