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)10static 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)16static 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)28size_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