【分享】使用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静态库
我的测试中,把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]
付汉杰 hankf@amd.com