前言:
我有时候对于程序调用不明显的时候,看程序很费劲,这时候我们希望把程序调用链打印出来。
对于库函数提供了线程接口。backtrace()、backtrace_symbols()、backtrace_symbols_fd()
可以在linux环境中通过man backtrace查看其用法,man手册里面还附着了一个示例,可以参考下。
其实网上的教程都是结合这个示例写的。我的也不例外。
移植代码:
直接上核心部分,如果你想看func_1的上级调用是谁,可以把这个接口放到这个函数里。
void print_stack(void)
{
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
* would produce similar output to the following: */
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);
}
示例代码:
直接在man backtrace基础上修改:
#include<execinfo.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void print_stack(void)
{
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
* would produce similar output to the following: */
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" means don't export the symbol... */
//static void myfunc2(void)
void myfunc2(void)
{
print_stack();
}
void myfunc1()
{
myfunc2();
}
int main(int argc,char *argv[])
{
myfunc1();
exit(EXIT_SUCCESS);
}
Makefile:
注意,在生成可执行文件的编译选项里加上-rdynamic ,否则你看到的只是调用栈地址。
#Makeile
main:main.o
gcc -rdynamic main.o -o main
main.o:main.c
gcc -c -g -Wall main.c -o main.o
.PHONY:clean
clean:
rm -rf *.o main
运行结果:
可以看到函数调用链是main->myfunc1->myfunc2->print_stack
backtrace() returned 6 addresses
./main(print_stack+0x2e) [0x400a54]
./main(myfunc2+0x9) [0x400b22]
./main(myfunc1+0x9) [0x400b2e]
./main(main+0x19) [0x400b4a]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f34f6502840]
./main(_start+0x29) [0x400959]
如果Makefile中不加-rdynamic选项,运行的结果如下:
可以看到,只有调用栈地址信息,没有函数名。但是可以借助符号表查看调用栈地址表示什么函数。
./main() [0x400784]
./main() [0x400852]
./main() [0x40085e]
./main() [0x40087a]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f301a281840]
./main() [0x400689]
需要先生成.out文件,
gcc main.c main.out
然后借助.out文件查看符号表。
nm -n main.out
打印的符号表如下:
对着打印的调用栈地址,查找符号表。可以看到调用栈地址和函数名称对应关系如下:
0000000000400756 T print_stack -> ./main() [0x400784]
0000000000400849 T myfunc2 -> ./main.out() [0x400852]
0000000000400855 T myfunc1 -> ./main.out() [0x40085e]
0000000000400861 T main -> ./main.out() [0x40087a]
【附】
符号表:
U backtrace@@GLIBC_2.2.5
U backtrace_symbols@@GLIBC_2.2.5
U exit@@GLIBC_2.2.5
U free@@GLIBC_2.2.5
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
U __libc_start_main@@GLIBC_2.2.5
U perror@@GLIBC_2.2.5
U printf@@GLIBC_2.2.5
U puts@@GLIBC_2.2.5
U __stack_chk_fail@@GLIBC_2.4
0000000000400588 T _init
0000000000400660 T _start
0000000000400690 t deregister_tm_clones
00000000004006d0 t register_tm_clones
0000000000400710 t __do_global_dtors_aux
0000000000400730 t frame_dummy
0000000000400756 T print_stack
0000000000400849 T myfunc2
0000000000400855 T myfunc1
0000000000400861 T main
0000000000400890 T __libc_csu_init
0000000000400900 T __libc_csu_fini
0000000000400904 T _fini
0000000000400910 R _IO_stdin_used
0000000000400950 r __GNU_EH_FRAME_HDR
0000000000400af0 r __FRAME_END__
0000000000600e10 t __frame_dummy_init_array_entry
0000000000600e10 t __init_array_start
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
0000000000600e18 t __init_array_end
0000000000600e20 d __JCR_END__
0000000000600e20 d __JCR_LIST__
0000000000600e28 d _DYNAMIC
0000000000601000 d _GLOBAL_OFFSET_TABLE_
0000000000601060 D __data_start
0000000000601060 W data_start
0000000000601068 D __dso_handle
0000000000601070 B __bss_start
0000000000601070 b completed.7594
0000000000601070 D _edata
0000000000601070 D __TMC_END__
0000000000601078 B _end
【附】man backtrace示例代码:
#include<execinfo.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void myfunc3(void)
{
int j, nptrs;
#define SIZE 100
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
* would produce similar output to the following: */
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" means don't export the symbol... */
//void myfunc2(void)
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);
}
参考:
man backtrace
https://blog.csdn.net/halazi100/article/details/78313271
https://www.cnblogs.com/lidabo/p/3635956.html