打印函数调用栈

前言:

我有时候对于程序调用不明显的时候,看程序很费劲,这时候我们希望把程序调用链打印出来。

对于库函数提供了线程接口。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

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值