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;
+}