From 4392ee46a9efd3b489eac57fc684c443caabcd5f Mon Sep 17 00:00:00 2001 From: Yong Cong Sin Date: Thu, 18 Jul 2024 15:29:20 +0800 Subject: [PATCH] arch: riscv: stacktrace: fix output without `ra` on the stack top Account for the scenario when we are doing `esf`-based unwinding from a function which doesn't have any callee. In this case the `ra` is not saved on the stack and the second function from the top of the frame could be missing. Signed-off-by: Yong Cong Sin Signed-off-by: Yong Cong Sin --- arch/riscv/core/stacktrace.c | 58 ++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/arch/riscv/core/stacktrace.c b/arch/riscv/core/stacktrace.c index c6d950b6f4131..cb130f6bfe180 100644 --- a/arch/riscv/core/stacktrace.c +++ b/arch/riscv/core/stacktrace.c @@ -90,7 +90,10 @@ static bool in_stack_bound(uintptr_t addr, const struct k_thread *const thread, static bool in_fatal_stack_bound(uintptr_t addr, const struct k_thread *const thread, const struct arch_esf *esf) { - if (!IS_ALIGNED(addr, sizeof(uintptr_t))) { + const uintptr_t align = + COND_CODE_1(CONFIG_FRAME_POINTER, (ARCH_STACK_PTR_ALIGN), (sizeof(uintptr_t))); + + if (!IS_ALIGNED(addr, align)) { return false; } @@ -134,22 +137,53 @@ static void walk_stackframe(stack_trace_callback_fn cb, void *cookie, const stru ra = csf->ra; } - for (int i = 0; (i < MAX_STACK_FRAMES) && vrfy(fp, thread, esf) && (fp > last_fp);) { - if (in_text_region(ra)) { - if (!cb(cookie, ra)) { - break; - } - /* - * Increment the iterator only if `ra` is within the text region to get the - * most out of it - */ - i++; + for (int i = 0; (i < MAX_STACK_FRAMES) && vrfy(fp, thread, esf) && (fp > last_fp); i++) { + if (in_text_region(ra) && !cb(cookie, ra)) { + break; } last_fp = fp; + /* Unwind to the previous frame */ frame = (struct stackframe *)fp - 1; - ra = frame->ra; + + if ((i == 0) && (esf != NULL)) { + /* Print `esf->ra` if we are at the top of the stack */ + if (in_text_region(esf->ra) && !cb(cookie, esf->ra)) { + break; + } + /** + * For the first stack frame, the `ra` is not stored in the frame if the + * preempted function doesn't call any other function, we can observe: + * + * .-------------. + * frame[0]->fp ---> | frame[0] fp | + * :-------------: + * frame[0]->ra ---> | frame[1] fp | + * | frame[1] ra | + * :~~~~~~~~~~~~~: + * | frame[N] fp | + * + * Instead of: + * + * .-------------. + * frame[0]->fp ---> | frame[0] fp | + * frame[0]->ra ---> | frame[1] ra | + * :-------------: + * | frame[1] fp | + * | frame[1] ra | + * :~~~~~~~~~~~~~: + * | frame[N] fp | + * + * Check if `frame->ra` actually points to a `fp`, and adjust accordingly + */ + if (vrfy(frame->ra, thread, esf)) { + fp = frame->ra; + frame = (struct stackframe *)fp; + } + } + fp = frame->fp; + ra = frame->ra; } } #else /* !CONFIG_FRAME_POINTER */