文章摘要:
backtrace(回溯)函数主要用于代码故障跟踪,输出错误现场的函数调用栈(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。
也可以在正常状态下使用该库,获取当前的函数调用栈;


函数说明:

#include <execinfo.h>
/*
 * 功能描述: 获取当前堆栈地址
 * 参数说明: buffer - 函数地址数组(传出)
 *          size   - 数组的最大长度 
 * 返 回 值:实际调用的层数
 */
int backtrace(void **buffer, int size);    


/*
 * 功能描述: 将地址转换成符号字符串数组
 * 参数说明: buffer - 函数地址数组
 *          size   - 有效地址的个数 
 * 返 回 值: 符号字符串数组指针(需要调用者释放)
 */
char **backtrace_symbols(void *const *buffer, int size);

   
/*
 * 功能描述: 将地址转换成符号字符串数组并写入指定文件
 * 参数说明: buffer - 函数地址数组
 *          size   - 有效地址的个数 
 *          fd     - 文件描述符
 * 返 回 值: 无
 */
void backtrace_symbols_fd(void *const *buffer, int size, int fd);

Linux例程:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<execinfo.h>
/*
 * 内存访问异常回调函数
 */
void segv_handle(int signum)
{
  int i;  
  size_t size;  
  void *pfunc[10];  // 函数指针数组(地址)
  char **func_symb; // 函数符号字符串数组指针(二维) 
  // 获取堆栈调用层数
  size = backtrace(pfunc, 10);  // 最大长度应该与指针数组一致
  // 获取符号
  func_symb = (char **)backtrace_symbols(pfunc, size);    
  // 打印输入相关信息
  fprintf(stderr, "Stack trace: %d\r\n",size);
  for (i = 0; i < size; i++)
  {
    fprintf(stderr, "%d %s \r\n", i, func_symb[i]);
  }        
  free(func_symb); // 释放动态申请的内存
  exit(1);    
} 
//-------------------------------------------------
void func2nd()
{
  char *p=NULL;
  *p = 'A';        // 制造内存访问异常
}

void func1st()
{
  func2nd();
}

int main(const int argc,const char* argv[])
{
  // 无效内存异常信号捕获,SegmentationViolation
  signal(SIGSEGV, segv_handle);
  func1st(); 
  return 0;
}

编译选项:

$ gcc -o BackTrace BackTrace.c -rdynamic

运行结果:

Stack trace:
0 ./BackTrace(segv_handle+0x54) [0x804882f] 
1 linux-gate.so.1(__kernel_sigreturn+0) [0xb77a9d1c] 
2 ./BackTrace(func2nd+0x10) [0x80488c7] 
3 ./BackTrace(func1st+0x8) [0x80488d4] 
4 ./BackTrace(main+0x28) [0x80488fe] 
5 /lib/i386-linux-gnu/i686/cmov/libc.so.6(__libc_start_main+0xf3) [0xb75f8a63] 
6 ./BackTrace() [0x8048701] 

-rdynamic 生成符号表,否则只有地址:

Stack trace:
0 ./BackTrace() [0x80485bf] 
1 linux-gate.so.1(__kernel_sigreturn+0) [0xb77ccd1c] 
2 ./BackTrace() [0x8048657] 
3 ./BackTrace() [0x8048664] 
4 ./BackTrace() [0x804868e] 
5 /lib/i386-linux-gnu/i686/cmov/libc.so.6(__libc_start_main+0xf3) [0xb761ba63] 
6 ./BackTrace() [0x8048491] 

需要采用addr2line命令来逐个查看:

# addr2line  -e BackTrace -f 0x8048657
func2nd
??:?


移植backtrace用于MCU平台

CmBacktrace (Cortex Microcontroller Backtrace)是一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库。
支持IAR、KEIL、GCC 编译器,支持裸机及RT-Thread,UCOS,FreeRTOS(需修改源码)等操作系统平台。

项目主页: https://github.com/armink/CmBacktrace

1.查看 \demos 目录下有没有合适自己的 Demo ,如有类似,则建议在其基础上修改
2.明确操作系统/裸机平台及 CPU 平台
3.将 \src 下的全部源文件添加至产品工程中,并保证源码目录被添加至头文件路径
4.cmb_fault.s 汇编文件(点击查看)可以选择性添加至工程,添加后需要把项目原有的 HardFault_Handler 注释掉
5.把 cm_backtrace_init 函数放在项目初始化地方执行
6.将 cm_backtrace_assert 放在项目的断言函数中执行,具体使用方法参照下面的 API 说明
7.如果第 4 步骤没有将 cmb_fault.s 汇编文件启用,则需要将 cm_backtrace_fault 放到故障处理函数(例如: HardFault_Handler )中执行,具体使用方法参照下面的 API 说明.