微信公众号:DPDK VPP源码分析
关注公众号可了解更多的学习资源,请留言:资料。问题或建议,请公众号留言;如果你觉得内容对你有帮助,欢迎赞赏[1]
异常调用栈信息跟踪
vpp代码中设置捕捉异常信号的函数unix_signal_handler,对一些信号SIGSEGV、SIGABRT、SIGILL等等会打印出异常的调用栈信息,方便我们定位问题。异常调用栈信息可以在系统日志中查询。通常我会使用journalctl -n xxx 来查询日志的打印。
在glibc头文件"execinfo.h"中声明了backtrace用于获取当前线程的函数调用堆栈
int backtrace(void **buffer,int size)
该函数用于获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小
在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址.
下面是vpp代码中clib_backtrace函数的定义,。
/* 使用 glibc backtrace 函数打印调用栈信息 */
#include
uwordclib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip){
int size;
void *array[20];
/* Also skip current frame. 跳过本函数*/
n_frames_to_skip += 1;
size = clib_min (ARRAY_LEN (array), max_callers + n_frames_to_skip);
size = backtrace (array, size);
uword i;
for (i = 0; i {
if (i >= n_frames_to_skip)
callers[i - n_frames_to_skip] = pointer_to_uword (array[i]);
}
if (i return 0;
else
return i - n_frames_to_skip;
}
下面截取
/* Address of callers: outer first, inner last.
最多打印15个*/
uword callers[15];
uword n_callers = clib_backtrace (callers, ARRAY_LEN (callers), 0);
int i;
for (i = 0; i {
vec_reset_length (syslog_msg);
syslog_msg = format (syslog_msg, "#%-2d 0x%016lx %U%c", i, callers[i],
format_clib_elf_symbol_with_address, callers[i], 0);
/*format_clib_elf_symbol_with_address会将根据函数地址打印出函数名称,具体底层实现待跟踪*/
/*打印系统日志*/
syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg);
个人测试举例
我们参考vpp的ASSERT可以写个myassert 用来对代码中异常分支进行捕捉,方便定位问题。
#include
#include
#include
#include
#include
#include
#define myassert(flg)\
do{\
int pid = getpid();\if(!flg)\
{\
printf("\r\n file:%s,Line:%d,fuc:%s\r\n",__FILE__,__LINE__,__FUNCTION__);\
kill(pid,SIGUSR1);\
}\
}while(0)
void dump(int signo){
void *buffer[30] = {0};
size_t size = 0;
size_t i = 0;
char ** strings = NULL;
size = backtrace(buffer,30);
strings = backtrace_symbols(buffer,size);
if(strings == NULL)
{
return;
}
for (i = 0 ; i {
printf("%s\n",strings[i]);
}
free(strings);
//exit(0);
}
void trace_3(){
int * p = NULL;
/*为空时表示是异常,触发函数调用栈打印*/
myassert((p != NULL));
}
void trace_2(){
trace_3();
}
void trace_1(){
trace_2();
}
int main(){
char c = 0;
/*用户捕捉myassert 信号*/
signal(SIGUSR1,dump);
/*捕捉assert 异常信号*/
signal(SIGABRT,dump);
trace_1();
c = getchar();
assert(0);
return;
}
代码运行后打印如下:
[jsh@localhost work]$ gcc backtrace.c -g -rdynamic
[jsh@localhost work]$
[jsh@localhost work]$ ./a.out
/*这里打印具体的函数名和代码行*/
file:backtrace.c,Line:52,fuc:trace_3
./a.out(dump+0x4c) [0x8048820]
[0xac9400]
./a.out(trace_2+0xb) [0x80488e1]
./a.out(trace_1+0xb) [0x80488ee]
./a.out(main+0x3b) [0x804892b]
/lib/libc.so.6(__libc_start_main+0xe6) [0x182ce6]
./a.out() [0x8048741]
/*这里是系统assert的打印如下*/
a.out: backtrace.c:77: main: Assertion `0' failed.
./a.out(dump+0x4c) [0x8048820]
[0xac9400]
/lib/libc.so.6(abort+0x17a) [0x1983aa]
/lib/libc.so.6(-0xff52f215) [0x18fdeb]
/lib/libc.so.6(-0xff52f15a) [0x18fea6]
./a.out() [0x8048958]
/lib/libc.so.6(__libc_start_main+0xe6) [0x182ce6]
./a.out() [0x8048741]
Aborted (core dumped)
总结
本文简单介绍了定位vpp异常的一点思路,可以通过journalctl -n xxx 来找到异常调用站信息来定位BUG。当然journalctl命令还是很强大的。查看所有内核和应用的日志,并且提供了很多可以检索的参数。
你们关于定位异常有什么高效的方法,欢迎交流