diff --git a/arch/xtensa/src/common/xtensa_backtrace.c b/arch/xtensa/src/common/xtensa_backtrace.c new file mode 100644 index 00000000000..27efd032e0c --- /dev/null +++ b/arch/xtensa/src/common/xtensa_backtrace.c @@ -0,0 +1,287 @@ +/**************************************************************************** + * arch/xtensa/src/common/xtensa_backtrace.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include "sched/sched.h" +#include "xtensa.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Convert return address to a valid pc */ + +#define MAKE_PC_FROM_RA(ra) ((ra) & 0x3fffffff) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifndef __XTENSA_CALL0_ABI__ +struct xtensa_windowregs_s +{ + uint32_t windowbase; + uint32_t windowstart; + uint32_t areg[16]; +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: get_window_regs + * + * Description: + * getfp() returns current frame pointer + * + ****************************************************************************/ + +#ifndef __XTENSA_CALL0_ABI__ +static void get_window_regs(struct xtensa_windowregs_s *frame) +inline_function +{ + __asm__ __volatile__("\trsr %0, WINDOWSTART\n": "=r"(frame->windowstart)); + __asm__ __volatile__("\trsr %0, WINDOWBASE\n": "=r"(frame->windowbase)); + + __asm__ __volatile__("\tmov %0, a0\n": "=r"(frame->areg[0])); + __asm__ __volatile__("\tmov %0, a1\n": "=r"(frame->areg[1])); + __asm__ __volatile__("\tmov %0, a2\n": "=r"(frame->areg[2])); + __asm__ __volatile__("\tmov %0, a3\n": "=r"(frame->areg[3])); + __asm__ __volatile__("\tmov %0, a4\n": "=r"(frame->areg[4])); + __asm__ __volatile__("\tmov %0, a5\n": "=r"(frame->areg[5])); + __asm__ __volatile__("\tmov %0, a6\n": "=r"(frame->areg[6])); + __asm__ __volatile__("\tmov %0, a7\n": "=r"(frame->areg[7])); + __asm__ __volatile__("\tmov %0, a8\n": "=r"(frame->areg[8])); + __asm__ __volatile__("\tmov %0, a9\n": "=r"(frame->areg[9])); + __asm__ __volatile__("\tmov %0, a10\n": "=r"(frame->areg[10])); + __asm__ __volatile__("\tmov %0, a11\n": "=r"(frame->areg[11])); + __asm__ __volatile__("\tmov %0, a12\n": "=r"(frame->areg[12])); + __asm__ __volatile__("\tmov %0, a13\n": "=r"(frame->areg[13])); + __asm__ __volatile__("\tmov %0, a14\n": "=r"(frame->areg[14])); + __asm__ __volatile__("\tmov %0, a15\n": "=r"(frame->areg[15])); +} +#endif + +/**************************************************************************** + * Name: backtrace_window + * + * Description: + * backtrace_window() parsing the return address in register window + * + ****************************************************************************/ + +#ifndef __XTENSA_CALL0_ABI__ +static int backtrace_window(FAR uintptr_t *base, FAR uintptr_t *limit, + struct xtensa_windowregs_s *frame, + FAR void **buffer, int size) +{ + uint32_t windowstart; + uint32_t ra; + uint32_t sp; + int index; + int i; + + /* Rotate WINDOWSTART to move the bit corresponding to + * the current window to the bit #0 + */ + + windowstart = (frame->windowstart << WSBITS | frame->windowstart) >> + frame->windowbase; + + /* Look for bits that are set, they correspond to valid windows. */ + + for (i = 0, index = WSBITS - 1; (index > 0) && (i < size); index--) + { + if (windowstart & (1 << index)) + { + ra = frame->areg[index * 4]; + sp = frame->areg[index * 4 + 1]; + + if (sp > limit || sp < base || ra == 0) + { + continue; + } + + buffer[i++] = MAKE_PC_FROM_RA(ra); + } + } + + return i; +} +#endif + +/**************************************************************************** + * Name: backtrace_stack + * + * Description: + * backtrace_stack() parsing the return address in stack + * + ****************************************************************************/ + +static int backtrace_stack(FAR uintptr_t *base, FAR uintptr_t *limit, + FAR uintptr_t *sp, FAR uintptr_t *ra, + FAR void **buffer, int size) +{ + int i = 0; + + if (ra) + { + buffer[i++] = MAKE_PC_FROM_RA((uintptr_t)ra); + } + + for (; i < size; sp = (FAR uintptr_t *)*(sp - 3), i++) + { + if (sp > limit || sp < base) + { + break; + } + + ra = (FAR uintptr_t *)*(sp - 4); + if (ra == NULL) + { + break; + } + + buffer[i] = MAKE_PC_FROM_RA((uintptr_t)ra); + } + + return i; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_backtrace + * + * Description: + * up_backtrace() returns a backtrace for the TCB, in the array + * pointed to by buffer. A backtrace is the series of currently active + * function calls for the program. Each item in the array pointed to by + * buffer is of type void *, and is the return address from the + * corresponding stack frame. The size argument specifies the maximum + * number of addresses that can be stored in buffer. If the backtrace is + * larger than size, then the addresses corresponding to the size most + * recent function calls are returned; to obtain the complete backtrace, + * make sure that buffer and size are large enough. + * + * Input Parameters: + * tcb - Address of the task's TCB + * buffer - Return address from the corresponding stack frame + * size - Maximum number of addresses that can be stored in buffer + * + * Returned Value: + * up_backtrace() returns the number of addresses returned in buffer + * + ****************************************************************************/ + +int up_backtrace(FAR struct tcb_s *tcb, FAR void **buffer, int size) +{ + FAR struct tcb_s *rtcb = running_task(); + irqstate_t flags; + int ret; + + if (size <= 0 || !buffer) + { + return 0; + } + + if (tcb == NULL || tcb == rtcb) + { + if (up_interrupt_context()) + { +#if CONFIG_ARCH_INTERRUPTSTACK > 15 + uintptr_t istackbase; +#ifdef CONFIG_SMP + istackbase = xtensa_intstack_alloc(); +#else + istackbase = &g_intstackalloc; +#endif + ret = bactrace_stack((FAR void *)istackbase, + (FAR void *)((uint32_t)&g_intstackalloc + + CONFIG_ARCH_INTERRUPTSTACK), + (FAR void *)up_getsp(), NULL, buffer, size); +#else + ret = backtrace_stack(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)up_getsp(), NULL, buffer, size); +#endif + ret += backtrace_stack(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + + rtcb->adj_stack_size, + (FAR void *)CURRENT_REGS[REG_A1], + (FAR void *)CURRENT_REGS[REG_A0], + &buffer[ret], size - ret); + } + else + { + /* Two steps for current task: + * + * 1. Look through the register window for the + * previous PCs in the call trace. + * + * 2. Look on the stack. + */ + +#ifndef __XTENSA_CALL0_ABI__ + static struct xtensa_windowregs_s frame; + + xtensa_window_spill(); + + get_window_regs(&frame); + + ret = backtrace_window(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + &frame, buffer, size); +#endif + ret += backtrace_stack(rtcb->stack_base_ptr, + rtcb->stack_base_ptr + rtcb->adj_stack_size, + (FAR void *)up_getsp(), NULL, buffer, size - ret); + } + } + else + { + /* For non-current task, only check in stack. */ + + flags = enter_critical_section(); + + ret = backtrace_stack(tcb->stack_base_ptr, + tcb->stack_base_ptr + tcb->adj_stack_size, + (FAR void *)tcb->xcp.regs[REG_A1], + (FAR void *)tcb->xcp.regs[REG_A0], + buffer, size); + + leave_critical_section(flags); + } + + return ret; +}