Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1505254
  • 博文数量: 108
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 997
  • 用 户 组: 普通用户
  • 注册时间: 2013-06-29 09:58
个人简介

兴趣是坚持一件事永不衰竭的动力

文章分类

全部博文(108)

文章存档

2021年(1)

2020年(10)

2019年(19)

2018年(9)

2016年(23)

2015年(43)

2013年(3)

我的朋友

分类: C/C++

2015-04-21 16:37:56

在看一些大中型的软件的源代码时,你是不是非常希望有一个工具能够方便的生成各个函数之间的调用关系图呢?

为了实现这个目标,你可以通过对源代码进行静态扫描得到函数的调用关系,但是你无法通过这种方法获得更多的信息,

(如:对某个函数的调用次数,被调用的函数执行了多长时间等,这些信息对于软件的优化具有很好的参考价值)除了

静态扫描之外,还存在一些动态的方法,即在程序的运行过程中记录相关的信息,不过这些动态的方法通常都需要有编

译器的支持,通过编译器在编译的过程中插入相应的代码。下面简单的介绍一下,GCC的function instrumentation

机制。

 

简单的来说,gcc function instrumentation就是在每个函数调用之前调用一个名为__cyg_profile_func_enter的函数,

在函数调用介绍的时候调用__cyg_profile_func_exit。这两个函数在gcc看来只是两个普通的函数,是可以由用户自己进行

定制的。下面先看个例子:

 


  1. #include   
  2. #define DUMP(func, call) /  
  3.     printf("%s: func = %p, called by = %p/n", __FUNCTION__, func, call)  
  4. void __attribute__((__no_instrument_function__))  
  5.     __cyg_profile_func_enter(void *this_func, void *call_site)  
  6. {  
  7.         DUMP(this_func, call_site);  
  8. }  
  9. void __attribute__((__no_instrument_function__))  
  10.     __cyg_profile_func_exit(void *this_func, void *call_site)  
  11. {  
  12.         DUMP(this_func, call_site);  
  13. }  
  14. main()  
  15. {  
  16.         puts("Hello World!");  
  17.         return 0;  
  18. }  
 


编译与运行:

$ gcc -finstrument-functions hello.c -o hello

$ ./hello

__cyg_profile_func_enter: func = 0x8048468, called by = 0xb7e36ebc

Hello World!

__cyg_profile_func_exit: func = 0x8048468, called by = 0xb7e36ebc

 

呵呵,是不是很神奇,只需要在软件中简单加入__cyg_profile_func_enter  __cyg_profile_func_exit这两个函数的定义,

然后再编译的时候加上-finstrument-functions选项,就可以在每个函数调用前后调用这两个函数获取你所感兴趣的信息。为了

对其具体的实现细节有所了解,可以通过”objdump -d hello“来看下程序反汇编之后的结果。


  1. 08048488 
    :  
  2.  8048488:   8d 4c 24 04             lea    0x4(%esp),%ecx  
  3.  804848c:   83 e4 f0                and    $0xfffffff0,%esp  
  4.  804848f:   ff 71 fc                pushl  0xfffffffc(%ecx)  
  5.  8048492:   55                      push   %ebp  
  6.  8048493:   89 e5                   mov    %esp,%ebp  
  7.  8048495:   53                      push   %ebx  
  8.  8048496:   51                      push   %ecx  
  9.  8048497:   83 ec 10                sub    $0x10,%esp  
  10.  804849a:   8b 45 04                mov    0x4(%ebp),%eax  
  11.  804849d:   89 44 24 04             mov    %eax,0x4(%esp)  
  12.  80484a1:   c7 04 24 88 84 04 08    movl   $0x8048488,(%esp)  
  13.  80484a8:   e8 87 ff ff ff          call   8048434 <__cyg_profile_func_enter>  
  14.  80484ad:   c7 04 24 03 86 04 08    movl   $0x8048603,(%esp)  
  15.  80484b4:   e8 8f fe ff ff          call   8048348   
  16.  80484b9:   bb 00 00 00 00          mov    $0x0,%ebx  
  17.  80484be:   8b 45 04                mov    0x4(%ebp),%eax  
  18.  80484c1:   89 44 24 04             mov    %eax,0x4(%esp)  
  19.  80484c5:   c7 04 24 88 84 04 08    movl   $0x8048488,(%esp)  
  20.  80484cc:   e8 8d ff ff ff          call   804845e <__cyg_profile_func_exit>  
  21.  80484d1:   89 d8                   mov    %ebx,%eax  
  22.  80484d3:   83 c4 10                add    $0x10,%esp  
  23.  80484d6:   59                      pop    %ecx  
  24.  80484d7:   5b                      pop    %ebx  
  25.  80484d8:   5d                      pop    %ebp  
  26.  80484d9:   8d 61 fc                lea    0xfffffffc(%ecx),%esp  
  27.  80484dc:   c3                      ret  
 


从上面的汇编代码可以看出,gcc是在进入函数完成堆栈的初始化之后调用__cyg_profile_func_enter的,在函数返回清理堆栈

之前调用__cyg_profile_func_exit的。

 

经过上面的步骤,我们还只能获取到函数调用时的信息,还没有获取到函数之间的调用关系,但是明白了上述的原理之后,参看参看

参考资料[1]就能够很容易的实现目标了。

 

参考资料

[1] http://www.ibm.com/developerworks/cn/linux/l-graphvis/

[2] http://blog.linux.org.tw/~jserv/archives/001870.html

阅读(2380) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~