CALL STACK TRACE GENERATION

原文发表在http://www.acsu.buffalo.edu/~charngda/backtrace.html(链接已失效),转载自http://cdunn2001.blogspot.com/2012_05_01_archive.html

Call stack trace ("backtrace") is very useful in debugging. Here are several ways to retrieve the backtrace in a user program.

(The contents are mostly from here and here)

EASIEST APPROACH: __BUILTIN_RETURN_ADDRESS

GCC has a built-in function to retrieve call stack trace's addresses. For example

void do_backtrace()

{

    printf("Frame 0: PC=%p\n", __builtin_return_address(0));

    printf("Frame 1: PC=%p\n", __builtin_return_address(1));

    printf("Frame 2: PC=%p\n", __builtin_return_address(2));

    printf("Frame 3: PC=%p\n", __builtin_return_address(3));

}


 

__builtin_return_address(0) is always current function's address. On the other hand,__builtin_return_address(1)__builtin_return_address(2), ... may not be available on all platforms.

WHAT TO DO WITH THESE ADDRESSES ?

Addresses can be mapped to the binary executable or dynamic link libraries. This is always doable even if the binary executable has been stripped off the symbols.

To see the mapping during runtime, parse the following plain-text file on the /proc file system:

/proc/self/maps

A utility called pmap can do the same.

If the address belongs to a DLL, it is possible to obtain the function name since DLLs are usually not stripped.

Addresses can be mapped to function names. Even if a binary executable is compiled without -g option, it still contains function names. To see the function names in the binary executable, do

nm -C -n a.out

To see the function names programmatically in the binary executable during run-time, read later paragraphs.

Addresses can be mapped to line numbers in source files. This extra information (in DWARF format) is added to the binary executable if it is compiled with -g option. To see line numbers in source files, do

objdump -WL a.out

objdump --dwarf=decodedline a.out

or even better:

addr2line -ifC a.out 0x123456

where 0x123456 is the address of interest. To see line numbers in source files programmatically during run-time, read later paragraphs.

APPROACH 2: BACKTRACE

backtrace and backtrace_symbols are functions in Glibc. To use backtrace_symbols, one must compile the program with -rdynamic option.

One does not need to compile with -g option (but -rdynamic option cannot be used together with -static option) since backtrace_symbols cannot retrieve line number information. Actually, one can even strip off the symbols, and the backtrace_symbols will still work. This is because when -rdynamic is used, all global symbols are also stored in .dynsym section in the ELF-formatted executable binary, and this section cannot be stripped away. (To see the content of .dynsym section, use readelf -s a.out command, or readelf -p .dynstr a.outcommand.)

backtrace_symbols obtains symbol information from .dynsym section.

(The main purpose of .dynsym section is for dynamic link libraries to expose their symbols so the runtime linker ld.so can find them.)

Here is the sample program:

#include <execinfo.h>

void do_backtrace()

{

    #define BACKTRACE_SIZ 100

    void    *array[BACKTRACE_SIZ];

    size_t  size, i;

    char    **strings;

    size = backtrace(array, BACKTRACE_SIZ);

    strings = backtrace_symbols(array, size);

    for (i = 0; i < size; ++i) {

        printf("%p : %s\n", array[i], strings[i]);

    }
    free(strings);

}

<span style="font-family:Arial;BACKGROUND-COLOR: #ffffff"></span>

For C++ programs, to get demangled names, use abi::__cxa_demangle (include the headercxxabi.h)

 

APPROACH 3: IMPROVED BACKTRACE

The backtrace_symbols in Glibc uses dladdr to obtain function names, but it cannot retrieve line numbers. Jeff Muizelaar has an improved version here which can do line numbers.

If the user program is compiled without any special command-line options, then one can obtain function names (of course, provided the binary executable is not stripped.) Better yet, -rdynamic compiler option is not needed.

If the user program is compiled with -g option, one can obtain both line numbers and function names.

Note that to compile Jeff Muizelaar's backtrace_symbols implementation, make sure the following two macros are defined and appears as the first two lines of a user program (they must precede before all #include ...):

#define __USE_GNU

#define _GNU_SOURCE

and one needs Binary File Descriptor (BFD) library, which is now part of GNU binutils when linking Jeff's code to the user program.

 

APPROACH 4: LIBUNWIND

libunwind does pretty much what the original backtrace/backtrace_symbols do. Its main purpose, however, is to unwind the stack programmatically (even more powerful thansetjmp/longjmp pair) through unw_step and unw_resume calls. One can also peek and modify the saved register values on stack via unw_get_regunw_get_fregunw_set_reg, and unw_set_freg calls.

If one just wants to retrieve the backtrace, use the following code:

#include <libunwind.h>

void do_backtrace()

{

    unw_cursor_t    cursor;

    unw_context_t   context;

    unw_getcontext(&context);

    unw_init_local(&cursor, &context);

    while (unw_step(&cursor) > 0) {

        unw_word_t  offset, pc;

        char        fname[64];
        unw_get_reg(&cursor, UNW_REG_IP, &pc);

        fname[0] = '\0';

        (void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);

        printf ("%p : (%s+0x%x) [%p]\n", pc, fname, offset, pc);

    }

}

 


 

and linked the user program with -lunwind -lunwind-x86_64.

There is no need to compile the user program with -g option.

POSTED BY CHRISTOPHER DUNN AT 11:24 AM 0 COMMENTS 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C中的stacktrace是指在程序运行过程中,记录函数调用关系和执行位置的一种机制。当程序发生错误或异常情况时,可以通过stacktrace来定位问题发生的位置,帮助开发人员进行调试和错误修复。 在C语言中,stacktrace可以通过获取函数调用栈来实现。函数调用栈是一个存储函数调用关系和局部变量的数据结构,栈顶表示当前正在执行的函数。可以通过以下几种方法来获取C的stacktrace: 1. 使用调试工具:在开发环境中,可以使用调试工具(如GDB)来获取stacktrace。通过设置断点或捕捉异常,可以在程序执行过程中暂停并查看函数调用栈的息。 2. 异常处理:在代码中可以使用异常处理机制来捕获异常,并将stacktrace作为异常息输出。可以使用`backtrace`和`backtrace_symbols`等函数获取函数调用栈的息,并将其打印出来。 3. 自定义实现:也可以自己编写代码来实现获取stacktrace的功能。使用函数指针的方式,将每个函数的调用关系保存在一个自定义的数据结构中,并在需要时打印出来。 无论使用哪种方法,获取stacktrace可以帮助开发人员更快地定位问题发生的位置,从而快速解决bug。在生产环境中,由于性能和安全等方面的考虑,通常需要做一些限制,避免泄露敏感息。因此,在实际应用中需要根据具体需求来选择合适的方法来获取stacktrace

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值