在rtems上手动实现backtrace
由于rtems没有Linux那种crash自动打印调用栈的机制,故需要手动实现。调用栈的实现依赖于fp指针,通过fp可以找到每一级函数的栈帧,根据arm函数调用规范,在栈帧中保存着pc、sp、lr、fp等信息,因此可以将每一级的pc值打出来就是我们需要的调用栈了,通过addr2line工具可以将pc指针解析为对应的行号,从而帮助我们精确定位问题
FP指针的获取
-mapcs
首先添加这个参数,使编译出来的代码遵守apcs标准,也就是使函数的栈帧保持如下的样子
-fno-omit-frame-pointer
其次,添加这个参数,用来防止fp指针被优化掉
有了以上两个选项,就可以通过fp一层一层找到上一级函数的栈帧,pc、lr、sp、fp相对于栈帧首地址都是固定偏移,因此有了fp,所有信息都可以找到了
对于调用栈的获取,主要是获取每一级函数的pc指针,然后通过addr2line工具得到对应的代码行
调用栈打印不完整
需要添加编译选项
-O0
否则很多函数被优化成inline,几乎看不到调用的中间过程
函数名的打印
添加以下编译选项
-mpoke-function-name
添加之后,编译的时候,将会为每一个函数生成符号名,保存到与函数首地址紧挨的部分,例如:
t0
.ascii "arm_poke_function_name", 0
.align
t1
.word 0xff000000 + (t1 - t0)
arm_poke_function_name
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
在函数的头部,将会存放一个word,值为0xff000000
和符号名地址偏移相与的结果,通过这个word,就可以找到函数名了
但是我们的调用栈拿到的是pc指针,根本不知道在自己在什么函数里,因此可以通过从pc指针出发,向上扫描0xff000000
这个魔鬼数字,进而拿到函数字符串的地址
thumb模式
另外,需要去掉-mthumb
选项。在thumb模式下,fp指针也可以获取,不过和arm32位模式的寄存器不一样,arm32的fp是r11,thumb模式是r7,thumb和arm32可能会来回切换,因此在实现调用栈的时候,将要时时刻刻知道调用函数所在的cpu状态,在栈帧中也没有保存cpsr寄存器,故目前没有方法实现thumb模式的调用栈打印
rtems上的实现
rtems异常机制
rtems_fatal
RTEMS_NO_RETURN RTEMS_INLINE_ROUTINE void rtems_fatal(
rtems_fatal_source fatal_source,
rtems_fatal_code error_code
)
rtems在检测到出错的时候,将会调用rtems_fatal
接口用来通知系统出错了,这个出错的位置可以在任何上下文,任务、中断、boot_card,异常处理函数等等,通过fatal_source
来告知,error_code
是一个uint32_t的值,具体含义由fatal_source来定义
fatal_source有如下值
typedef enum {
/**
* @brief Errors of the core system.
*
* @see Internal_errors_Core_list.
*/
INTERNAL_ERROR_CORE = 0,
/**
* @brief Errors of the RTEMS API.
*/
INTERNAL_ERROR_RTEMS_API = 1,
/**
* @brief Errors of the POSIX API.
*/
INTERNAL_ERROR_POSIX_API = 2,
/**
* @brief Fatal source for the block device cache.
*
* @see rtems_bdbuf_fatal_code.
*/
RTEMS_FATAL_SOURCE_BDBUF = 3,
/**
* @brief Fatal source for application specific errors.
*
* The fatal code is application specific.
*/
RTEMS_FATAL_SOURCE_APPLICATION = 4,
/**
* @brief Fatal source of exit().
*
* The fatal code is the exit() status code.
*/
RTEMS_FATAL_SOURCE_EXIT = 5,
/**
* @brief Fatal source for BSP errors.
*
* The fatal codes are defined in <bsp/fatal.h>. Examples are interrupt and
* exception initialization.
*
* @see bsp_fatal_code and bsp_fatal().
*/
RTEMS_FATAL_SOURCE_BSP = 6,
/**
* @brief Fatal source of assert().
*
* The fatal code is the pointer value of the assert context.
*
* @see rtems_assert_context.
*/
RTEMS_FATAL_SOURCE_ASSERT = 7,
/**
* @brief Fatal source of the stack checker.
*
* The fatal code is the object name of the executing task.
*/
RTEMS_FATAL_SOURCE_STACK_CHECKER = 8,
/**
* @brief Fatal source of the exceptions.
*
* The fatal code is the pointer value of the exception frame pointer.
*
* @see rtems_exception_frame and rtems_exception_frame_print().
*/
RTEMS_FATAL_SOURCE_EXCEPTION = 9,
/**
* @brief Fatal source of SMP domain.
*
* @see SMP_Fatal_code.
*/
RTEMS_FATAL_SOURCE_SMP = 10,
/**
* @brief Fatal source of rtems_panic().
*
* @see rtem
*/
RTEMS_FATAL_SOURCE_PANIC = 11,
/**
* @brief Fatal source for invalid C program heap frees via free().
*
* The fatal code is the bad pointer.
*/
RTEMS_FATAL_SOURCE_INVALID_HEAP_FREE = 12,
/**
* @brief Fatal source for heap errors.
*
* The fatal code is the address to a heap error context (Heap_Error_context).
*/
RTEMS_FATAL_SOURCE_HEAP = 13,
/**
* @brief The last available fatal source.
*
* This enum value ensures that the enum type needs at least 32-bits for
* architectures with short enums.
*/
RTEMS_FATAL_SOURCE_LAST = 0xffffffff
} Internal_errors_Source;
其中RTEMS_FATAL_SOURCE_APPLICATION
是在任务上下文触发,这个可以确定,RTEMS_FATAL_SOURCE_EXCEPTION
这个是在异常上下文触发,这个也可以确定,其他都是不确定的
User extensions
User extensions是rtems的一种插件机制,rtems事先在系统运行的关键位置埋了点,如果想在这些时间点实现一些功能,实现一个插件就可以了
插件原型如下
typedef struct {
User_extensions_thread_create_extension thread_create;
User_extensions_thread_start_extension thread_start;
User_extensions_thread_restart