针对大型软件中,在调试问题时,经常会发现某些业务逻辑的调用路径很难查找,或者说是不清楚程序的运行栈。
想查看程序的运行栈有几种方法:
(1)程序编译时,带 -g 选项,直接在某个函数(接口)添加一行导致死机的代码,死机堆栈就可以找到程序的运行栈,很龊但是有时候挺管用;
(2)在函数里面添加__builtin_return_address来查找程序的运行栈;
本篇介绍一下的__builtin_return_address函数的使用
__builtin_return_address 函数的作用是返回所在函数被一级函数调用后,退出的地址(通常为return)。
英文原意是“When inlining the expected behavior is that the function returns the address of the function that is returned to”
此处参考网络上一个示例 :
示例代码test_build_return_address.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#define MAX_LEVEL 4
void sigfunc(int signo)
{
printf("%s(0): %p\n", __func__, __builtin_return_address(0));
printf("%s(1): %p\n", __func__, __builtin_return_address(1));
printf("%s(2): %p\n", __func__, __builtin_return_address(2));
printf("%s(3): %p\n", __func__, __builtin_return_address(3));
printf("%s(4): %p\n", __func__, __builtin_return_address(4));
exit(1);
}
int b()
{
printf("%s(0): %p\n", __func__, __builtin_return_address(0));
while(1)
{
sleep(1);
}
}
int a(int temp)
{
temp += 1;
printf("%s(0): %p\n", __func__, __builtin_return_address(0));
b();
return temp;
}
int c(int temp)
{
temp++;
printf("%s(0): %p\n", __func__, __builtin_return_address(0));
a(123);
return temp;
}
int main()
{
// signal(SIGINT, sigfunc);
// a(123);
c(123);
return 0;
}
编译(gcc -g test_build_return_address.c -o test_build_return_address),运行(gdb -q test_build_return_address)
会打印运行栈的地址:
[root@localhost test]# gdb -q test_build_return_address
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) run
Starting program: /home/kehuanyu/test/test_build_return_address
c(0): 0x8048516
a(0): 0x80484f4
b(0): 0x80484be
接着ctrl+c ,通过gdb指令来找到对应的代码:
Program received signal SIGINT, Interrupt.
0x003d1402 in __kernel_vsyscall ()
(gdb) l *0x80484be
0x80484be is in a (test_build_return_address.c:32).
27 temp += 1;
28 printf("%s(0): %p\n", __func__, __builtin_return_address(0));
29
30 b();
31
32 return temp;
33 }
34
35 int c(int temp)
36 {
(gdb) l *0x80484f4
0x80484f4 is in c (test_build_return_address.c:43).
38
39 printf("%s(0): %p\n", __func__, __builtin_return_address(0));
40
41 a(123);
42
43 return temp;
44 }
45
46 int main()
47 {
(gdb) l *0x8048516
0x8048516 is in main (test_build_return_address.c:51).
46 int main()
47 {
48 // signal(SIGINT, sigfunc);
49 // a(123);
50 c(123);
51 return 0;
52 }
其实获取到运行栈地址之后,也可以通过ELF处理库直接解析符号列表。
友情链接:
http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html 关于函数调用返回信息的__builtin_系列函数
void * __builtin_return_address (unsigned int level)
void * __builtin_extract_return_addr (void *addr)
void * __builtin_frob_return_address (void *addr)
void * __builtin_frame_address (unsigned int level)
http://www.gnu.org/software/gdb/ gdb的使用
https://gcc.gnu.org/ gcc编译器