| /* |
| * Copyright (c) 2023 KNS Group LLC (YADRO) |
| * Copyright (c) 2020 Yonatan Goldschmidt <yon.goldschmidt@gmail.com> |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| |
| static bool valid_stack(uintptr_t addr, k_tid_t current) |
| { |
| return current->stack_info.start <= addr && |
| addr < current->stack_info.start + current->stack_info.size; |
| } |
| |
| /* interruption stack frame */ |
| struct isf { |
| uint32_t ebp; |
| uint32_t ecx; |
| uint32_t edx; |
| uint32_t eax; |
| uint32_t eip; |
| }; |
| |
| /* |
| * This function use frame pointers to unwind stack and get trace of return addresses. |
| * Return addresses are translated in corresponding function's names using .elf file. |
| * So we get function call trace |
| */ |
| size_t arch_perf_current_stack_trace(uintptr_t *buf, size_t size) |
| { |
| if (size < 1U) |
| return 0; |
| |
| size_t idx = 0; |
| |
| const struct isf * const isf = |
| *((struct isf **)(((void **)_current_cpu->irq_stack)-1)); |
| /* |
| * In x86 (arch/x86/core/ia32/intstub.S) %eip and %ebp |
| * are saved at the beginning of _interrupt_enter in order, that described |
| * in struct esf. Core switch %esp to |
| * _current_cpu->irq_stack and push %esp on irq stack |
| * |
| * The following lines lines do the reverse things to get %eip and %ebp |
| * from thread stack |
| */ |
| void **fp = (void **)isf->ebp; |
| |
| /* |
| * %ebp is frame pointer. |
| * |
| * stack frame in memory: |
| * (addresses growth up) |
| * .... |
| * ra |
| * %ebp (next) <- %ebp (curr) |
| * .... |
| */ |
| |
| buf[idx++] = (uintptr_t)isf->eip; |
| while (valid_stack((uintptr_t)fp, _current)) { |
| if (idx >= size) |
| return 0; |
| |
| buf[idx++] = (uintptr_t)fp[1]; |
| void **new_fp = (void **)fp[0]; |
| |
| /* |
| * anti-infinity-loop if |
| * new_fp can't be smaller than fp, cause the stack is growing down |
| * and trace moves deeper into the stack |
| */ |
| if (new_fp <= fp) { |
| break; |
| } |
| fp = new_fp; |
| } |
| |
| return idx; |
| } |