backtrace, backtrace_symbols, backtrace_symbols_fd

需要已掌握:动态符号表、符号表相关内容

backtrace, backtrace_symbols, backtrace_symbols_fd - 用于支持应用程序自调试

提要

#include <execinfo.h>

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);

描述

backtrace() 用于获取调用程序的堆栈。获取的堆栈信息存放在参数buffer中,它是一个指针数组。堆栈表示程序对该函数的一连串的调用。buffer指向的数组中的每一项都是void*类型,存放的是来自相应堆栈帧的返回地址。size参数指定了buffer中最多可存放几个地址。backtrace() 函数的返回值是实际获取的指针个数,最大不超过size大小。如果堆栈大小大于size指定的值,则只能获取到最近调用该函数的部分堆栈帧地址;为了获得完整的堆栈,请确保buffersize设置的足够大。

给定backtrace()buffer返回的地址集,backtrace_symbols()可以将地址转为用符号地址描述的字符串数组size参数指定了该字符串数组中包含的地址数(即backtrace()的返回值)。每个符号地址中包含了函数名(如果可以确定的话),十六进制的函数偏移量和十六进制的实际返回地址。backtrace_symbols()的返回值是一个指向字符串数组的指针,它的大小和buffer相同。该数组的内存是backtrace_symbols()分配的(调用malloc),并且必须被调用者释放(free)。(指针数组指向的字符串不需要也不应该被释放。)

backtrace_symbols()返回的字符串都是malloc出来的,但是不要最后一个一个的free,因为backtrace_symbols()是根据backtrace()给出的堆栈层数,一次性的malloc出来一块内存来存放结果字符串的。所以,像示例中的代码一样,只需要在最后,freebacktrace_symbols()的返回指针就OK了。

backtrace_symbols_fd()采用与backtrace_symbols()相同的buffersize参数,但它不向调用方返回字符串数组,而是将字符串(每行一个)写入文件描述符fdbacktrace_symbols_fd()不调用malloc,因此可以在调用该函数可能发生失败的情况下使用,具体请参见注意

返回值

backtrace()返回buffer中返回的地址数,该地址数<=size指定的大小。如果返回值小于size,则存储完整堆栈;如果它等于size,则它可能已被截断,在这种情况下,可能不会返回最开始堆栈帧的地址。

成功时,backtrace_symbols()返回一个指向调用者所分配的数组的指针;失败时,返回NULL。

属性

InterfaceAttributeValue
backtrace()Thread safetyMT-Safe
backtrace_symbols()Thread safetyMT-Safe
backtrace_symbols_fd()Thread safetyMT-Safe

注意

这些函数对函数的返回地址如何存储在堆栈上进行了一些假设。注意以下几点:

  • Omission of the frame pointers (as implied by any of gcc’s nonzero optimization levels) may cause these assumptions to be violated.(删除堆栈帧指针会导致无法正确解析堆栈内容)
  • 内联函数没有堆栈帧。
  • 尾部调用优化会导致一个堆栈帧替换另一个堆栈帧。
  • backtrace()backtrace_symbols_fd()不显式调用malloc(),但它们是libgcc的一部分,libgcc第一次使用时会动态加载。动态加载通常会触发对malloc的调用。如果需要对这两个函数进行某些调用而不分配内存(例如,在信号处理程序中),则需要确保事先加载libgcc
  • 某些编译器的优化选项对获取正确的调用堆栈有干扰

例子

下面的程序演示了backtrace()backtrace_symbols_fd()的使用。下面的shell会话显示了运行程序时可能看到的内容:

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BT_BUF_SIZE 100

void myfunc3(void)
{
    int j, nptrs;
    void *buffer[BT_BUF_SIZE];
    char **strings;

    nptrs = backtrace(buffer, BT_BUF_SIZE);
    printf("backtrace() returned %d addresses\n", nptrs);


  
    // 下方整个实现打印堆栈的代码可以用一行代码代替,即
    // backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO);
    strings = backtrace_symbols(buffer, nptrs);
    if (strings == NULL)
    {
        perror("backtrace_symbols");
        exit(EXIT_FAILURE);
    }

    for (j = 0; j < nptrs; j++)
        printf("%s\n", strings[j]);

    free(strings);
}

// static 的作用是不导出符号,即在打印堆栈时不显示该函数名“myfunc2”
static void myfunc2(void)
{
    myfunc3();
}

void myfunc(int ncalls)
{
    if (ncalls > 1)
        myfunc(ncalls - 1);
    else
        myfunc2();
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "%s num-calls\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    myfunc(atoi(argv[1]));
    exit(EXIT_SUCCESS);
}

编译并运行:

# 在使用GNU ld的系统中,为了支持函数名功能,需要传递-rdynamic标志给链接器
cc -rdynamic prog.c  -o prog 

运行结果

在这里插入图片描述

具体参见

man 3 backtrace

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:我们首先将用最基本的编译方式将他们编译成一个可执行文件并执行,如下: gcc -g -rdynamic backtrace.c add.c dump.c -o backtrace ./backtrace =========>>>catch signal 11 <<<========= Dump stack start... backtrace() returned 8 addresses \[00\] ./backtrace(dump+0x1f) \[0x400a9b\] \[01\] ./backtrace(signal_handler+0x31) \[0x400b63\] \[02\] /lib/x86_64-linux-gnu/libc.so.6(+0x36150) \[0x7f86afc7e150\] \[03\] ./backtrace(add1+0x1a) \[0x400a3e\] \[04\] ./backtrace(add+0x1c) \[0x400a71\] \[05\] ./backtrace(main+0x2f) \[0x400a03\] \[06\] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) \[0x7f86afc6976d\] \[07\] ./backtrace() \[0x400919\] Dump stack end... 段错误 (核心已转储) 。 引用\[2\]:  backtrace_symbols生成的字符串占用的内存是malloc出来的,但是是一次性malloc出来的,释放是只需要一次性释放返回的二级指针即可。 void backtrace_symbols_fd(void *const *buffer, int size, int fd); 。 引用\[3\]:其中参数 -L. -ladd为编译时链接当前目录的libadd.so;参数-Wl,-rpath=.为指定程序执行时动态链接库搜索路径为当前目录,否则会出现执行找不到libadd.so的错误。然后执行backtrace程序结果如下: ./backtrace =========>>>catch signal 11 <<<========= Dump stack start... backtrace() returned 8 addresses \[00\] ./backtrace(dump+0x1f) \[0x400a53\] \[01\] ./backtrace(signal_handler+0x31) \[0x400b1b\] \[02\] /lib/x86_64-linux-gnu/libc.so.6(+0x36150) \[0x7f8583672150\] \[03\] ./libadd.so(add1+0x1a) \[0x7f85839fa5c6\] \[04\] ./libadd.so(add+0x1c) \[0x7f85839fa5f9\] \[05\] ./backtrace(main+0x2f) \[0x400a13\] \[06\] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) \[0x7f858365d76d\] \[07\] ./backtrace() \[0x400929\] Dump stack end... 段错误 (核心已转储) 。 问题:backtrace_symbols_fd函数的作用是什么? 回答: backtrace_symbols_fd函数的作用是将backtrace函数返回的地址信息转换成可读的字符串,并将其写入指定的文件描述符中。\[2\]这个函数的参数包括一个指向地址数组的指针,数组的大小和一个文件描述符。函数会将地址数组中的每个地址转换成对应的函数名和偏移量,并将结果写入指定的文件描述符中。 #### 引用[.reference_title] - *1* *3* [c语言 backtrace](https://blog.csdn.net/w1050321758/article/details/125833542)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [backtrace函数](https://blog.csdn.net/weixin_33672400/article/details/93449156)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值