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 /* 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)37size_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