Linux调试之打印应用调用栈
在Linux中程序发生异常时,将函数的调用栈打印出来,可以大大提高定位效率。在自己写应用模块的时候,尽量在自己的代码中首先实现类似与内核里面的dump_stack的函数,这样在出现问题的时候,可以大大提高问题排查效率。Linux中提供了三个函数用来获取调用栈:
/* 获取函数调用栈 */
int backtrace(void **buffer, int size);
/* 将调用栈中的函数地址转化为函数名称 并返回一个字符串数组 */
char **backtrace_symbols(void *const *buffer, int size);
/* 将调用栈中的函数地址转化为函数名称 并将其定入到文件中 */
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
代码实例:
/*************************************************************************
File Name: backtrace.c
Author: Albert Jie
Mail: huangjieajy@163.com
Created Time: 2020年08月25日 09时55分41秒 ************************************************************************/
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
/* 调用栈的最大深度 */
#define MAX_STACK_DEPTH 16
/* 打印调用栈函数 */
void dump_stack_user() {
void *stack_trace[MAX_STACK_DEPTH] = {0};
char **stack_strings = NULL;
int stack_depth = 0;
int i = 0;
/* 获取栈中各层调用函数地址 */
stack_depth = backtrace(stack_trace, MAX_STACK_DEPTH);
/* 查找符号表将函数调用地址转换为函数名称 */
stack_strings = (char **)backtrace_symbols(stack_trace, stack_depth);
if (NULL == stack_strings) {
printf(" Memory is not enough while dump Stack Trace! \r\n");
return;
}
/* 打印调用栈 */
printf(" Stack Trace: \r\n");
for (i = 0; i < stack_depth; ++i) {
printf(" [%d] %s \r\n", i, stack_strings[i]);
}
/* 获取函数名称时申请的内存需要自行释放 */
free(stack_strings);
stack_strings = NULL;
return;
}
/* 测试函数 2 */
void b() {
dump_stack_user();
return;
}
/* 测试函数 1 */
void a() {
b();
return;
}
/* 主函数 */
int main(int argc, char *argv[]) {
a();
return 0;
}
注意源文件在编译时需要加上-rdynamic
参数,以得到符号名称:
gcc -rdynamic backtrace.c -o backtrace
执行./backtrace
运行程序,输出如下:这里就将整个代码的执行路径的调用栈打印出来了。
Stack Trace:
[0] ./backtrace(dump_stack_user+0x64) [0x55b014401a7e]
[1] ./backtrace(b+0xe) [0x55b014401b5c]
[2] ./backtrace(a+0xe) [0x55b014401b6d]
[3] ./backtrace(main+0x19) [0x55b014401b89]
[4] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f82986abbf7]
[5] ./backtrace(_start+0x2a) [0x55b01440193a]
这里我们已经讲解了调用栈如何打印,下篇文章我们讲解一下函数调用栈的实现原理,以及栈帧相关的知识。