linux调试之打印应用调用栈

linux应用程序中打印调用栈

Posted by Albert on August 25, 2020

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] 

这里我们已经讲解了调用栈如何打印,下篇文章我们讲解一下函数调用栈的实现原理,以及栈帧相关的知识。