浏览代码

[kservice] Enhance support for backtrace service (#9037)

[feat] Enhance support for backtrace service

rt_backtrace_formatted_print() and rt_backtrace_to_buffer() to help
debug routines.

Also, following modification are included:
- make rt_backtrace_frame patchable with weak attr
- replace lwp backtrace with sync output

Signed-off-by: Shell <smokewood@qq.com>
Shell 10 月之前
父节点
当前提交
e5b7f3fdd8
共有 6 个文件被更改,包括 173 次插入32 次删除
  1. 1 1
      include/rthw.h
  2. 5 1
      include/rtthread.h
  3. 1 1
      libcpu/aarch64/common/trap.c
  4. 1 1
      libcpu/risc-v/virt64/trap.c
  5. 7 7
      src/Kconfig
  6. 158 21
      src/kservice.c

+ 1 - 1
include/rthw.h

@@ -22,7 +22,7 @@
 
 #include <rtdef.h>
 
-#if defined (RT_USING_CACHE) || defined(RT_USING_SMP)
+#if defined (RT_USING_CACHE) || defined(RT_USING_SMP) || defined(RT_HW_INCLUDE_CPUPORT)
 #include <cpuport.h> /* include spinlock, cache ops, etc. */
 #endif
 

+ 5 - 1
include/rtthread.h

@@ -296,6 +296,7 @@ void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block));
  * heap memory interface
  */
 void rt_system_heap_init(void *begin_addr, void *end_addr);
+void rt_system_heap_init_generic(void *begin_addr, void *end_addr);
 
 void *rt_malloc(rt_size_t size);
 void rt_free(void *ptr);
@@ -729,7 +730,10 @@ void rt_kputs(const char *str);
 
 rt_err_t rt_backtrace(void);
 rt_err_t rt_backtrace_thread(rt_thread_t thread);
-rt_err_t rt_backtrace_frame(struct rt_hw_backtrace_frame *frame);
+rt_err_t rt_backtrace_frame(rt_thread_t thread, struct rt_hw_backtrace_frame *frame);
+rt_err_t rt_backtrace_formatted_print(rt_ubase_t *buffer, long buflen);
+rt_err_t rt_backtrace_to_buffer(rt_thread_t thread, struct rt_hw_backtrace_frame *frame,
+                                long skip, rt_ubase_t *buffer, long buflen);
 
 #if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
 rt_device_t rt_console_set_device(const char *name);

+ 1 - 1
libcpu/aarch64/common/trap.c

@@ -370,7 +370,7 @@ void rt_hw_trap_exception(struct rt_hw_exp_stack *regs)
 #endif
 
     struct rt_hw_backtrace_frame frame = {.fp = regs->x29, .pc = regs->pc};
-    rt_backtrace_frame(&frame);
+    rt_backtrace_frame(rt_thread_self(), &frame);
     rt_hw_cpu_shutdown();
 }
 

+ 1 - 1
libcpu/risc-v/virt64/trap.c

@@ -366,7 +366,7 @@ void handle_trap(rt_size_t scause, rt_size_t stval, rt_size_t sepc, struct rt_hw
             .pc = sepc
         };
         rt_kprintf("fp = %p", frame.fp);
-        rt_backtrace_frame(&frame);
+        rt_backtrace_frame(rt_thread_self(), &frame);
 
         RT_ASSERT(0);
     }

+ 7 - 7
src/Kconfig

@@ -114,13 +114,6 @@ config RT_TICK_PER_SECOND
     help
         System's tick frequency, Hz.
 
-config RT_USING_OVERFLOW_CHECK
-    bool "Using stack overflow checking"
-    default y
-    help
-        Enable thread stack overflow checking. The stack overflow is checking when
-        each thread switch.
-
 config RT_USING_HOOK
     bool "Enable system hook"
     default y
@@ -261,6 +254,13 @@ menuconfig RT_USING_DEBUG
             depends on RT_USING_SMP
             default y if RT_USING_SMART
             default n
+
+        config RT_USING_OVERFLOW_CHECK
+            bool "Using stack overflow checking"
+            default y
+            help
+                Enable thread stack overflow checking. The stack overflow is checking when
+                each thread switch.
     endif
 
 menu "Inter-Thread communication"

+ 158 - 21
src/kservice.c

@@ -25,11 +25,15 @@
  * 2022-08-30     Yunjie       make rt_vsnprintf adapt to ti c28x (16bit int)
  * 2023-02-02     Bernard      add Smart ID for logo version show
  * 2023-10-16     Shell        Add hook point for rt_malloc services
+ * 2023-10-21     Shell        support the common backtrace API which is arch-independent
  * 2023-12-10     xqyjlj       perf rt_hw_interrupt_disable/enable, fix memheap lock
  * 2024-03-10     Meco Man     move std libc related functions to rtklibc
  */
 
 #include <rtthread.h>
+
+/* include rt_hw_backtrace macro defined in cpuport.h */
+#define RT_HW_INCLUDE_CPUPORT
 #include <rthw.h>
 
 #define DBG_TAG           "kernel.service"
@@ -86,6 +90,34 @@ rt_weak void rt_hw_cpu_shutdown(void)
     return;
 }
 
+/**
+ * @note can be overridden by cpuport.h which is defined by a specific arch
+ */
+#ifndef RT_HW_BACKTRACE_FRAME_GET_SELF
+
+#ifdef __GNUC__
+    #define RT_HW_BACKTRACE_FRAME_GET_SELF(frame) do {          \
+        (frame)->fp = (rt_base_t)__builtin_frame_address(0U);   \
+        (frame)->pc = ({__label__ pc; pc: (rt_base_t)&&pc;});   \
+    } while (0)
+
+#else
+    #define RT_HW_BACKTRACE_FRAME_GET_SELF(frame) do {  \
+        (frame)->fp = 0;                                \
+        (frame)->pc = 0;                                \
+    } while (0)
+
+#endif /* __GNUC__ */
+
+#endif /* RT_HW_BACKTRACE_FRAME_GET_SELF */
+
+/**
+ * @brief Get the inner most frame of target thread
+ *
+ * @param thread the thread which frame belongs to
+ * @param frame the specified frame to be unwound
+ * @return rt_err_t 0 is succeed, otherwise a failure
+ */
 rt_weak rt_err_t rt_hw_backtrace_frame_get(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
 {
     RT_UNUSED(thread);
@@ -95,6 +127,13 @@ rt_weak rt_err_t rt_hw_backtrace_frame_get(rt_thread_t thread, struct rt_hw_back
     return -RT_ENOSYS;
 }
 
+/**
+ * @brief Unwind the target frame
+ *
+ * @param thread the thread which frame belongs to
+ * @param frame the specified frame to be unwound
+ * @return rt_err_t 0 is succeed, otherwise a failure
+ */
 rt_weak rt_err_t rt_hw_backtrace_frame_unwind(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
 {
     RT_UNUSED(thread);
@@ -356,27 +395,34 @@ rt_weak int rt_kprintf(const char *fmt, ...)
 RTM_EXPORT(rt_kprintf);
 #endif /* RT_USING_CONSOLE */
 
-#ifdef __GNUC__
+/**
+ * @brief Print backtrace of current thread to system console device
+ *
+ * @return rt_err_t 0 is success, otherwise a failure
+ */
 rt_weak rt_err_t rt_backtrace(void)
 {
-    struct rt_hw_backtrace_frame frame = {
-        .fp = (rt_base_t)__builtin_frame_address(0U),
-        .pc = ({__label__ pc; pc: (rt_base_t)&&pc;})
-    };
-    rt_hw_backtrace_frame_unwind(rt_thread_self(), &frame);
-    return rt_backtrace_frame(&frame);
-}
+    struct rt_hw_backtrace_frame frame;
+    rt_thread_t thread = rt_thread_self();
 
-#else /* otherwise not implemented */
-rt_weak rt_err_t rt_backtrace(void)
-{
-   /* LOG_W cannot work under this environment */
-    rt_kprintf("%s is not implemented\n", __func__);
-    return -RT_ENOSYS;
+    RT_HW_BACKTRACE_FRAME_GET_SELF(&frame);
+    if (!frame.fp)
+        return -RT_EINVAL;
+
+    /* we don't want this frame to be printed which is nearly garbage info */
+    rt_hw_backtrace_frame_unwind(thread, &frame);
+
+    return rt_backtrace_frame(thread, &frame);
 }
-#endif
 
-rt_err_t rt_backtrace_frame(struct rt_hw_backtrace_frame *frame)
+/**
+ * @brief Print backtrace from frame to system console device
+ *
+ * @param thread the thread which frame belongs to
+ * @param frame where backtrace starts from
+ * @return rt_err_t 0 is success, otherwise a failure
+ */
+rt_weak rt_err_t rt_backtrace_frame(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
 {
     long nesting = 0;
 
@@ -385,7 +431,7 @@ rt_err_t rt_backtrace_frame(struct rt_hw_backtrace_frame *frame)
     while (nesting < RT_BACKTRACE_LEVEL_MAX_NR)
     {
         rt_kprintf(" 0x%lx", (rt_ubase_t)frame->pc);
-        if (rt_hw_backtrace_frame_unwind(rt_thread_self(), frame))
+        if (rt_hw_backtrace_frame_unwind(thread, frame))
         {
             break;
         }
@@ -395,6 +441,89 @@ rt_err_t rt_backtrace_frame(struct rt_hw_backtrace_frame *frame)
     return RT_EOK;
 }
 
+/**
+ * @brief Print backtrace from buffer to system console
+ *
+ * @param buffer where traced frames saved
+ * @param buflen number of items in buffer
+ * @return rt_err_t 0 is success, otherwise a failure
+ */
+rt_weak rt_err_t rt_backtrace_formatted_print(rt_ubase_t *buffer, long buflen)
+{
+    rt_kprintf("please use: addr2line -e rtthread.elf -a -f");
+
+    for (size_t i = 0; i < buflen && buffer[i] != 0; i++)
+    {
+        rt_kprintf(" 0x%lx", (rt_ubase_t)buffer[i]);
+    }
+
+    rt_kprintf("\n");
+    return RT_EOK;
+}
+
+
+/**
+ * @brief Print backtrace from frame to the given buffer
+ *
+ * @param frame where backtrace starts from. NULL if it's the current one
+ * @param skip the number of frames to discarded counted from calling function.
+ *             Noted that the inner most frame is always discarded and not counted,
+ *             which is obviously reasonable since that's this function itself.
+ * @param buffer where traced frames saved
+ * @param buflen max number of items can be saved in buffer. If there are no more
+ *               than buflen items to be saved, there will be a NULL after the
+ *               last saved item in the buffer.
+ * @return rt_err_t 0 is success, otherwise a failure
+ */
+rt_weak rt_err_t rt_backtrace_to_buffer(rt_thread_t thread,
+                                        struct rt_hw_backtrace_frame *frame,
+                                        long skip,
+                                        rt_ubase_t *buffer,
+                                        long buflen)
+{
+    long nesting = 0;
+    struct rt_hw_backtrace_frame cur_frame;
+
+    if (!thread)
+        return -RT_EINVAL;
+
+    RT_ASSERT(rt_object_get_type(&thread->parent) == RT_Object_Class_Thread);
+
+    if (!frame)
+    {
+        frame = &cur_frame;
+        RT_HW_BACKTRACE_FRAME_GET_SELF(frame);
+        if (!frame->fp)
+            return -RT_EINVAL;
+    }
+
+    /* discard frames as required. The inner most is always threw. */
+    do {
+        rt_hw_backtrace_frame_unwind(thread, frame);
+    } while (skip-- > 0);
+
+    while (nesting < buflen)
+    {
+        *buffer++ = (rt_ubase_t)frame->pc;
+        if (rt_hw_backtrace_frame_unwind(thread, frame))
+        {
+            break;
+        }
+        nesting++;
+    }
+
+    if (nesting < buflen)
+        *buffer = RT_NULL;
+
+    return RT_EOK;
+}
+
+/**
+ * @brief Print backtrace of a thread to system console device
+ *
+ * @param thread which call stack is traced
+ * @return rt_err_t 0 is success, otherwise a failure
+ */
 rt_err_t rt_backtrace_thread(rt_thread_t thread)
 {
     rt_err_t rc;
@@ -404,7 +533,7 @@ rt_err_t rt_backtrace_thread(rt_thread_t thread)
         rc = rt_hw_backtrace_frame_get(thread, &frame);
         if (rc == RT_EOK)
         {
-            rc = rt_backtrace_frame(&frame);
+            rc = rt_backtrace_frame(thread, &frame);
         }
     }
     else
@@ -643,7 +772,14 @@ rt_inline void _slab_info(rt_size_t *total,
 #define _MEM_INFO(...)
 #endif
 
-static void _rt_system_heap_init(void *begin_addr, void *end_addr)
+/**
+ * @brief This function will do the generic system heap initialization.
+ *
+ * @param begin_addr the beginning address of system page.
+ *
+ * @param end_addr the end address of system page.
+ */
+void rt_system_heap_init_generic(void *begin_addr, void *end_addr)
 {
     rt_ubase_t begin_align = RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE);
     rt_ubase_t end_align   = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_ALIGN_SIZE);
@@ -657,7 +793,8 @@ static void _rt_system_heap_init(void *begin_addr, void *end_addr)
 }
 
 /**
- * @brief This function will init system heap.
+ * @brief This function will init system heap. User can override this API to
+ *        complete other works, like heap sanitizer initialization.
  *
  * @param begin_addr the beginning address of system page.
  *
@@ -665,7 +802,7 @@ static void _rt_system_heap_init(void *begin_addr, void *end_addr)
  */
 rt_weak void rt_system_heap_init(void *begin_addr, void *end_addr)
 {
-    _rt_system_heap_init(begin_addr, end_addr);
+    rt_system_heap_init_generic(begin_addr, end_addr);
 }
 
 /**