简介:
在github 上发现个基于RT-thread 系统的RISC-V 架构的backtrace 工具(https://github.com/Yaochenger/RvBacktrace) 正好使用手里的GD32VF103 芯片上移植体验该功能。
RvBacktrace 的移植适配相对容易只有三个.c文件和一个.h文件加入到工程编译即可,移植适配有两种方式。

方式1通过在链接脚本中添加代码段的符号,来查找栈帧中的地址信息来回溯。

方式2 通过编译选项,使用特定的S0 寄存器作为栈帧保存寄存器来进行栈回溯,本次移植适配我们使用方式2。
对应栈回溯算法如下
#include "../include/rvbacktrace.h"
#if defined(BACKTRACE_USE_FP)
static rt_uint32_t _rt_susrstack;
static rt_uint32_t _rt_eusrstack;
static rt_thread_t _backtrace_thread;
static rt_thread_t _backtrace_threadn;
static rt_object_t * thread_object_table = RT_NULL;
extern unsigned int rvstack_frame[STACK_FRAME_LEN]; // stack frame
extern unsigned int rvstack_frame_len; // stack frame len
static void walk_stackframe(int (*print_func)(const char *fmt, ...))
{
rt_uint32_t num = 0;
_backtrace_thread = rt_thread_self(); // get current thread
_rt_susrstack = (rt_uint32_t)(uintptr_t)_backtrace_thread->stack_addr; // stack start address
_rt_eusrstack = (rt_uint32_t)(uintptr_t)(_backtrace_thread->stack_addr + _backtrace_thread->stack_size); // stack end address
unsigned long sp, fp, ra, pc; // stack pointer, frame pointer, return address, program counter
struct stackframe *frame;
const register unsigned long current_sp __asm__("sp"); // get current stack pointer
sp = current_sp;
fp = (unsigned long)__builtin_frame_address(0); // get current frame pointer
print_func("Current Thread Name: %s \n", _backtrace_thread->parent.name);
while (1)
{
frame = (struct stackframe *)(fp - BACKTRACE_LEN); // get frame pointer
if ((rt_uint32_t *)frame > (rt_uint32_t *)(uintptr_t)_rt_eusrstack)
{
rvstack_frame_len = num;
return;
}
sp = fp; // get stack pointer
fp = frame->s_fp; // get frame pointer
ra = frame->s_ra; // get return address
pc = frame->s_ra - 4; // get program counter
// print stack interval, return address, program counter
print_func("[%d]Stack interval :[0x%016lx - 0x%016lx] ra 0x%016lx pc 0x%016lx\n", num, sp, fp, ra, pc);
rvstack_frame[num] = pc; // save stack frame address
num++;
}
}
#if defined(BACKTRACE_ALL)
static void walk_stackframe_all(int (*print_func)(const char *fmt, ...))
{
rt_uint32_t num = 0, i = 0;
int thread_object_len = 0;
unsigned long sp, fp, ra, pc; // stack pointer, frame pointer, return address, program counter
struct stackframe *frame;
thread_object_len = rt_object_get_length(RT_Object_Class_Thread);
if (thread_object_len == RT_NULL)
{
return;
}
thread_object_table = (rt_object_t *) rt_malloc((sizeof(rt_object_t) * thread_object_len));
RT_ASSERT(thread_object_table != RT_NULL);
rt_object_get_pointers(RT_Object_Class_Thread, (rt_object_t *) thread_object_table, thread_object_len);
for (i = 0; i < thread_object_len; i++)
{
_backtrace_threadn = (rt_thread_t) thread_object_table[i];
if (_backtrace_threadn == (rt_thread_t) rt_thread_self())
{
continue;
}
_rt_susrstack = (rt_uint32_t) (uintptr_t) _backtrace_threadn->stack_addr; // stack start address
_rt_eusrstack = (rt_uint32_t) (uintptr_t) (_backtrace_threadn->stack_addr + _backtrace_threadn->stack_size); // stack end address
print_func("------------------------------Thread: %s backtrace------------------------------\r\n",
_backtrace_threadn->parent.name);
print_func("[%d]Thread Name: %s \n", i, _backtrace_threadn->parent.name);
sp = (unsigned long) _backtrace_threadn->sp;
fp = ((rt_ubase_t *) (_backtrace_threadn->sp))[BACKTRACE_FP_POS]; // get current frame pointer
while (1)
{
frame = (struct stackframe *) (fp - BACKTRACE_LEN); // get frame pointer
if ((rt_uint32_t *) frame > (rt_uint32_t *) (uintptr_t) _rt_eusrstack)
{
rvstack_frame_len = num;
rvbacktrace_addr2line((rt_uint32_t *) &rvstack_frame[0], print_func);
num = 0;
break;
}
sp = fp; // get stack pointer
fp = frame->s_fp; // get frame pointer
ra = frame->s_ra; // get return address
pc = frame->s_ra - 4; // get program counter
// print stack interval, return address, program counter
print_func("[%d]Stack interval :[0x%016lx - 0x%016lx] ra 0x%016lx pc 0x%016lx\n", num, sp, fp, ra, pc);
rvstack_frame[num] = pc; // save stack frame address
num++;
}
}
print_func("Thread Total Num: %d\n", thread_object_len);
}
#endif /* BACKTRACE_ALL */
// backtrace function
void rvbacktrace_fno(int (*print_func)(const char *fmt, ...))
{
print_func("\r\n---- RV_Backtrace Call Frame Start: ----\r\n");
print_func("###Please consider the value of ra as accurate and the value of sp as only for reference###\n");
print_func("------------------------------Thread: %s backtrace------------------------------\r\n", ((rt_thread_t)rt_thread_self())->parent.name);
walk_stackframe(print_func);
rvbacktrace_addr2line((rt_uint32_t *)&rvstack_frame[0], print_func); // addr2line function
#if defined (BACKTRACE_ALL_THREAD)
print_func("\r\n");
walk_stackframe_all(print_func);
#endif /* BACKTRACE_ALL_THREAD */
print_func("---- RV_Backtrace Call Frame End:----\r\n");
print_func("\r\n");
}
MSH_CMD_EXPORT_ALIAS(rvbacktrace_fno, rv_backtrace_all, backtrace all threads);
#endif /* BACKTRACE_USE_FP */将源代码加入工程编译,便添加编译选项 -fno-omit-frame-pointer

编译完成后添加测试以下测试代码。
void backtrace(int argc, char* argv[])
{
rvbacktrace();
}
MSH_CMD_EXPORT(backtrace, risc-v back trace)运行输出如下

使用 addr2line -e rtthread.elf -a -f 8003e30 8003e6c 800264e 800354a 80035b6 8003a66 800108a 查看调用栈
输出结果如下跟实际的调用关系也是一致

我要赚赏金
