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