【分享】使用GNU backtrace打印当前的函数调用关系(backtrace)

【分享】使用GNU backtrace打印当前的函数调用关系(backtrace)

概述

作者: 付汉杰 hankf@xilinx.com hankf@amd.com

通过GDB等调试器,可以检查一个软件线程当前的函数调用关系(backtrace),也就是a调用b,b调用c,c调用d之类的。
当出现异常时,Linux kerenl会自动打印当前的函数调用关系(backtrace),为定位问题提供了不少信息。
在Linux应用程序中,也可以打印当前的函数调用关系(backtrace),GNU为此提供了backtrace ( )和backtrace_symbols( )。以前曾经测试过,发现没有生效,backtrace ( )返回0。
最近测试,发现backtrace ( )能返回大于0的数,说明工作正常。 另外,在编译器增加选项“-fno-omit-frame-pointer”,在连接器增加选项“-rdynamic”,可以打印出更多信息。

GNU backtrace 代码

GNU关于生成函数调用关系(backtrace)的文章在GNU backtrace

其中的示例代码如下

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

/* Obtain a backtrace and print it to stdout. */
void
print_trace (void)
{
  void *array[10];
  char **strings;
  int size, i;

  size = backtrace (array, 10);
  strings = backtrace_symbols (array, size);
  if (strings != NULL)
  {

    printf ("Obtained %d stack frames.\n", size);
    for (i = 0; i < size; i++)
      printf ("%s\n", strings[i]);
  }

  free (strings);
}

/* A dummy function to make the backtrace more interesting. */
void
dummy_function (void)
{
  print_trace ();
}

int
main (void)
{
  dummy_function ();
  return 0;
}

我的测试中,把print_trace()放在一个独立的文件中,编译成一个静态库。

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

/* Obtain a backtrace and print it to stdout.
   https://www.gnu.org/software/libc/manual/html_node/Backtraces.html 
 */
void print_trace (void)
{
  void *array[32];
  char **strings;
  int size, i;

  // actually return addresses obtained by inspecting the stack
  //    one return address per stack frame.
  // frame pointer elimination will stop backtrace
  size = backtrace (array, 32);

  // Translates the return addresses obtained an array of strings.
  // Each string includes the function name, an offset into the function,
  //     and the actual return address (in hexadecimal).
  strings = backtrace_symbols (array, size);
  if (strings != NULL)
  {

    printf ("Obtained %d stack frames.\n", size);
    for (i = 0; i < size; i++)
      printf ("%s\n", strings[i]);
  }

  free (strings);
}

静态库的编译信息如下

18:08:06 **** Incremental Build of configuration Debug for project gnu_backtrace ****
make all 
'Building file: ../src/gnu_print_backtrace.c'
'Invoking: ARM v8 Linux gcc compiler'
aarch64-linux-gnu-gcc -Wall -O0 -g3 -fno-omit-frame-pointer -c -fmessage-length=0 -MT"src/gnu_print_backtrace.o" -static  -mcpu=cortex-a72 -MMD -MP -MF"src/gnu_print_backtrace.d" -MT"src/gnu_print_backtrace.o" -o "src/gnu_print_backtrace.o" "../src/gnu_print_backtrace.c"
'Finished building: ../src/gnu_print_backtrace.c'
' '
'Building target: libgnu_backtrace.a'
'Invoking: ARM v8 Linux archiver'
aarch64-linux-gnu-ar -r  "libgnu_backtrace.a"  ./src/gnu_print_backtrace.o   
aarch64-linux-gnu-ar: creating libgnu_backtrace.a
'Finished building target: libgnu_backtrace.a'
' '
18:08:09 Build Finished (took 2s.641ms)

测试应用程序

实际调用的print_trace()的代码是一个测试应用程序,代码如下:

#include <stdio.h>

#include "gnu_print_backtrace.h"

int func_level_3( void )
{
    printf("In Func: %s, at Line: %d\n", __func__, __LINE__ );
    print_trace ( );

    return 0;
}

int func_level_2( void )
{
    printf("In Func: %s, at Line: %d\n", __func__, __LINE__ );
    func_level_3( );

    return 0;
}

int func_level_1( void )
{
    printf("In Func: %s, at Line: %d\n", __func__, __LINE__ );
    func_level_2( );

    return 0;
}


int main()
{
    printf("Hello World\n");
    func_level_1( );

    return 0;
}

测试的应用程序的编译信息如下

18:10:05 **** Incremental Build of configuration Debug for project gnu_backtrace_test ****
make all 
'Building file: ../src/helloworld.c'
'Invoking: ARM v8 Linux gcc compiler'
aarch64-linux-gnu-gcc -Wall -O0 -g3 -fno-omit-frame-pointer -I../../gnu_backtrace/src/ -c -fmessage-length=0 -MT"src/helloworld.o" -mcpu=cortex-a72 -MMD -MP -MF"src/helloworld.d" -MT"src/helloworld.o" -o "src/helloworld.o" "../src/helloworld.c"
'Finished building: ../src/helloworld.c'
' '
'Building target: gnu_backtrace_test.elf'
'Invoking: ARM v8 Linux gcc linker'
aarch64-linux-gnu-gcc -L../../gnu_backtrace/Debug/ -mcpu=cortex-a72 -rdynamic -o "gnu_backtrace_test.elf"  ./src/helloworld.o   -lgnu_backtrace
'Finished building target: gnu_backtrace_test.elf'
' '
'Invoking: ARM v8 Linux Print Size'
aarch64-linux-gnu-size gnu_backtrace_test.elf  |tee "gnu_backtrace_test.elf.size"
   text	   data	    bss	    dec	    hex	filename
   3478	    672	      8	   4158	   103e	gnu_backtrace_test.elf
'Finished building: gnu_backtrace_test.elf.size'
' '
18:10:09 Build Finished (took 4s.45ms)

测试的应用程序的运行信息如下:

root@dapd-0330-tpg-peta:~# uname -a
Linux dapd-0330-tpg-peta 5.10.0-xilinx-v2021.2 #1 SMP Wed Apr 6 02:57:39 UTC 2022 aarch64 GNU/Linux

root@dapd-0330-tpg-peta:~# ldd ./gnu_backtrace_test.elf
        linux-vdso.so.1 (0x0000ffff8db0c000)
        libc.so.6 => /lib/libc.so.6 (0x0000ffff8d95a000)
        /lib/ld-linux-aarch64.so.1 (0x0000ffff8dadb000)

root@dapd-0330-tpg-peta:~# /lib/libc.so.6
GNU C Library (GNU libc) stable release version 2.32.
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 10.2.0.
libc ABIs: UNIQUE ABSOLUTE
For bug reporting instructions, please see:
<https://www.gnu.org/software/libc/bugs.html>.

root@dapd-0330-tpg-peta:~# ./gnu_backtrace_test_rdynamic.elf
Hello World
In Func: func_level_1, at Line: 41
In Func: func_level_2, at Line: 33
In Func: func_level_3, at Line: 25
Obtained 7 stack frames.
./gnu_backtrace_test_rdynamic.elf(print_trace+0x14) [0xaaaad7af0c6c]
./gnu_backtrace_test_rdynamic.elf(func_level_3+0x24) [0xaaaad7af0bc8]
./gnu_backtrace_test_rdynamic.elf(func_level_2+0x24) [0xaaaad7af0bf8]
./gnu_backtrace_test_rdynamic.elf(func_level_1+0x24) [0xaaaad7af0c28]
./gnu_backtrace_test_rdynamic.elf(main+0x18) [0xaaaad7af0c4c]
/lib/libc.so.6(__libc_start_main+0xe8) [0xffff9973f878]
./gnu_backtrace_test_rdynamic.elf(+0xac8) [0xaaaad7af0ac8]
posted @ 2022-04-13 09:24  HankFu  阅读(459)  评论(0编辑  收藏  举报