You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/09/16 02:32:47 UTC
[incubator-nuttx] 03/04: arch:xtensa: add up_backtrace support
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit 6e0f84dc88b92b5280721c7b7f8bb3e6e6a02289
Author: zhuyanlin <zh...@xiaomi.com>
AuthorDate: Wed Sep 15 15:00:04 2021 +0800
arch:xtensa: add up_backtrace support
Up_backtrace can be backtrace from task or interrupt.
Signed-off-by: zhuyanlin <zh...@xiaomi.com>
---
arch/xtensa/src/common/xtensa_backtrace.c | 287 ++++++++++++++++++++++++++++++
1 file changed, 287 insertions(+)
diff --git a/arch/xtensa/src/common/xtensa_backtrace.c b/arch/xtensa/src/common/xtensa_backtrace.c
new file mode 100644
index 0000000..27efd03
--- /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 <nuttx/config.h>
+#include <nuttx/arch.h>
+
+#include <arch/irq.h>
+#include <arch/xtensa/core.h>
+
+#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;
+}