arch: x86: intel64: make perf stack thrace func

Implement stack trace function for x86_64 arch, that get required
thread register values and unwind stack with it.

Originally-by: Yonatan Goldschmidt <yon.goldschmidt@gmail.com>
Signed-off-by: Mikhail Kushnerov <m.kushnerov@yadro.com>
This commit is contained in:
Mikhail Kushnerov 2024-05-23 14:27:44 +03:00 committed by Anas Nashif
parent a74474c1f9
commit 1588390907
2 changed files with 68 additions and 0 deletions

View file

@ -20,3 +20,4 @@ zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD intel64/irq_offload.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE intel64/userspace.S)
zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE intel64/tls.c)
zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP intel64/coredump.c)
zephyr_library_sources_ifdef(CONFIG_PROFILING_PERF intel64/perf.c)

View file

@ -0,0 +1,67 @@
/*
* 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;
}
/*
* 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;
/*
* In x86_64 (arch/x86/core/intel64/locore.S) %rip and %rbp
* are always saved in _current->callee_saved before calling
* handler function if interrupt is not nested
*
* %rip points the location where interrupt was occurred
*/
buf[idx++] = (uintptr_t)_current->callee_saved.rip;
void **fp = (void **)_current->callee_saved.rbp;
/*
* %rbp is frame pointer.
*
* stack frame in memory:
* (addresses growth up)
* ....
* ra
* %rbp (next) <- %rbp (curr)
* ....
*/
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;
}