[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>
This commit is contained in:
parent
1869c543a6
commit
e5b7f3fdd8
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
#include <rtdef.h>
|
#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. */
|
#include <cpuport.h> /* include spinlock, cache ops, etc. */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -296,6 +296,7 @@ void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block));
|
||||||
* heap memory interface
|
* heap memory interface
|
||||||
*/
|
*/
|
||||||
void rt_system_heap_init(void *begin_addr, void *end_addr);
|
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_malloc(rt_size_t size);
|
||||||
void rt_free(void *ptr);
|
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(void);
|
||||||
rt_err_t rt_backtrace_thread(rt_thread_t thread);
|
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)
|
#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
|
||||||
rt_device_t rt_console_set_device(const char *name);
|
rt_device_t rt_console_set_device(const char *name);
|
||||||
|
|
|
@ -370,7 +370,7 @@ void rt_hw_trap_exception(struct rt_hw_exp_stack *regs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct rt_hw_backtrace_frame frame = {.fp = regs->x29, .pc = regs->pc};
|
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();
|
rt_hw_cpu_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -366,7 +366,7 @@ void handle_trap(rt_size_t scause, rt_size_t stval, rt_size_t sepc, struct rt_hw
|
||||||
.pc = sepc
|
.pc = sepc
|
||||||
};
|
};
|
||||||
rt_kprintf("fp = %p", frame.fp);
|
rt_kprintf("fp = %p", frame.fp);
|
||||||
rt_backtrace_frame(&frame);
|
rt_backtrace_frame(rt_thread_self(), &frame);
|
||||||
|
|
||||||
RT_ASSERT(0);
|
RT_ASSERT(0);
|
||||||
}
|
}
|
||||||
|
|
14
src/Kconfig
14
src/Kconfig
|
@ -114,13 +114,6 @@ config RT_TICK_PER_SECOND
|
||||||
help
|
help
|
||||||
System's tick frequency, Hz.
|
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
|
config RT_USING_HOOK
|
||||||
bool "Enable system hook"
|
bool "Enable system hook"
|
||||||
default y
|
default y
|
||||||
|
@ -261,6 +254,13 @@ menuconfig RT_USING_DEBUG
|
||||||
depends on RT_USING_SMP
|
depends on RT_USING_SMP
|
||||||
default y if RT_USING_SMART
|
default y if RT_USING_SMART
|
||||||
default n
|
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
|
endif
|
||||||
|
|
||||||
menu "Inter-Thread communication"
|
menu "Inter-Thread communication"
|
||||||
|
|
181
src/kservice.c
181
src/kservice.c
|
@ -25,11 +25,15 @@
|
||||||
* 2022-08-30 Yunjie make rt_vsnprintf adapt to ti c28x (16bit int)
|
* 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-02-02 Bernard add Smart ID for logo version show
|
||||||
* 2023-10-16 Shell Add hook point for rt_malloc services
|
* 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
|
* 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
|
* 2024-03-10 Meco Man move std libc related functions to rtklibc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <rtthread.h>
|
#include <rtthread.h>
|
||||||
|
|
||||||
|
/* include rt_hw_backtrace macro defined in cpuport.h */
|
||||||
|
#define RT_HW_INCLUDE_CPUPORT
|
||||||
#include <rthw.h>
|
#include <rthw.h>
|
||||||
|
|
||||||
#define DBG_TAG "kernel.service"
|
#define DBG_TAG "kernel.service"
|
||||||
|
@ -86,6 +90,34 @@ rt_weak void rt_hw_cpu_shutdown(void)
|
||||||
return;
|
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_weak rt_err_t rt_hw_backtrace_frame_get(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
|
||||||
{
|
{
|
||||||
RT_UNUSED(thread);
|
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;
|
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_weak rt_err_t rt_hw_backtrace_frame_unwind(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
|
||||||
{
|
{
|
||||||
RT_UNUSED(thread);
|
RT_UNUSED(thread);
|
||||||
|
@ -356,27 +395,34 @@ rt_weak int rt_kprintf(const char *fmt, ...)
|
||||||
RTM_EXPORT(rt_kprintf);
|
RTM_EXPORT(rt_kprintf);
|
||||||
#endif /* RT_USING_CONSOLE */
|
#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)
|
rt_weak rt_err_t rt_backtrace(void)
|
||||||
{
|
{
|
||||||
struct rt_hw_backtrace_frame frame = {
|
struct rt_hw_backtrace_frame frame;
|
||||||
.fp = (rt_base_t)__builtin_frame_address(0U),
|
rt_thread_t thread = rt_thread_self();
|
||||||
.pc = ({__label__ pc; pc: (rt_base_t)&&pc;})
|
|
||||||
};
|
RT_HW_BACKTRACE_FRAME_GET_SELF(&frame);
|
||||||
rt_hw_backtrace_frame_unwind(rt_thread_self(), &frame);
|
if (!frame.fp)
|
||||||
return rt_backtrace_frame(&frame);
|
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)
|
* @brief Print backtrace from frame to system console device
|
||||||
{
|
*
|
||||||
/* LOG_W cannot work under this environment */
|
* @param thread the thread which frame belongs to
|
||||||
rt_kprintf("%s is not implemented\n", __func__);
|
* @param frame where backtrace starts from
|
||||||
return -RT_ENOSYS;
|
* @return rt_err_t 0 is success, otherwise a failure
|
||||||
}
|
*/
|
||||||
#endif
|
rt_weak rt_err_t rt_backtrace_frame(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
|
||||||
|
|
||||||
rt_err_t rt_backtrace_frame(struct rt_hw_backtrace_frame *frame)
|
|
||||||
{
|
{
|
||||||
long nesting = 0;
|
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)
|
while (nesting < RT_BACKTRACE_LEVEL_MAX_NR)
|
||||||
{
|
{
|
||||||
rt_kprintf(" 0x%lx", (rt_ubase_t)frame->pc);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -395,6 +441,89 @@ rt_err_t rt_backtrace_frame(struct rt_hw_backtrace_frame *frame)
|
||||||
return RT_EOK;
|
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 rt_backtrace_thread(rt_thread_t thread)
|
||||||
{
|
{
|
||||||
rt_err_t rc;
|
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);
|
rc = rt_hw_backtrace_frame_get(thread, &frame);
|
||||||
if (rc == RT_EOK)
|
if (rc == RT_EOK)
|
||||||
{
|
{
|
||||||
rc = rt_backtrace_frame(&frame);
|
rc = rt_backtrace_frame(thread, &frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -643,7 +772,14 @@ rt_inline void _slab_info(rt_size_t *total,
|
||||||
#define _MEM_INFO(...)
|
#define _MEM_INFO(...)
|
||||||
#endif
|
#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 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);
|
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.
|
* @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_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue