diff --git a/include/rthw.h b/include/rthw.h index 67a44acd17..f7712cb52f 100644 --- a/include/rthw.h +++ b/include/rthw.h @@ -22,7 +22,7 @@ #include -#if defined (RT_USING_CACHE) || defined(RT_USING_SMP) +#if defined (RT_USING_CACHE) || defined(RT_USING_SMP) || defined(RT_HW_INCLUDE_CPUPORT) #include /* include spinlock, cache ops, etc. */ #endif diff --git a/include/rtthread.h b/include/rtthread.h index 386fbaf47e..87383d3428 100644 --- a/include/rtthread.h +++ b/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); diff --git a/libcpu/aarch64/common/trap.c b/libcpu/aarch64/common/trap.c index 3be1578eb4..ca6ad1761f 100644 --- a/libcpu/aarch64/common/trap.c +++ b/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(); } diff --git a/libcpu/risc-v/virt64/trap.c b/libcpu/risc-v/virt64/trap.c index f34f46c9e9..4366a5b2c0 100644 --- a/libcpu/risc-v/virt64/trap.c +++ b/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); } diff --git a/src/Kconfig b/src/Kconfig index 0443366051..9718c65c72 100644 --- a/src/Kconfig +++ b/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" diff --git a/src/kservice.c b/src/kservice.c index 49305a0f35..f3ccaba802 100644 --- a/src/kservice.c +++ b/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 + +/* include rt_hw_backtrace macro defined in cpuport.h */ +#define RT_HW_INCLUDE_CPUPORT #include #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(); + + 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); } -#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; -} -#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); } /**