gcc查看函数调用的方法

使用过此方法,很好用。 曾经遇到过全局变量被非法越界改掉的情况,就是用此方法解决。

觉得自己需要记住。

 

原始来源如下:

 http://blog.csdn.net/unbutun/article/details/6564706

 

GCC就像一个巨大的宝藏,只要你愿意花时间,总能淘到好东西。

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

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

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

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

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

机制。

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

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

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

view plaincopy to clipboardprint?
01.#include <stdio.h>  
02.#define DUMP(func, call) /  
03.    printf("%s: func = %p, called by = %p/n", __FUNCTION__, func, call)  
04.void __attribute__((__no_instrument_function__))  
05.    __cyg_profile_func_enter(void *this_func, void *call_site)  
06.{  
07.        DUMP(this_func, call_site);  
08.}  
09.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.} 
#include <stdio.h>
#define DUMP(func, call) /
    printf("%s: func = %p, called by = %p/n", __FUNCTION__, func, call)
void __attribute__((__no_instrument_function__))
    __cyg_profile_func_enter(void *this_func, void *call_site)
{
        DUMP(this_func, call_site);
}
void __attribute__((__no_instrument_function__))
    __cyg_profile_func_exit(void *this_func, void *call_site)
{
        DUMP(this_func, call_site);
}
main()
{
        puts("Hello World!");
        return 0;
}
 

编译与运行:

$ 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“来看下程序反汇编之后的结果。

view plaincopy to clipboardprint?
01.08048488 <main>:  
02. 8048488:   8d 4c 24 04             lea    0x4(%esp),%ecx  
03. 804848c:   83 e4 f0                and    $0xfffffff0,%esp  
04. 804848f:   ff 71 fc                pushl  0xfffffffc(%ecx)  
05. 8048492:   55                      push   %ebp  
06. 8048493:   89 e5                   mov    %esp,%ebp  
07. 8048495:   53                      push   %ebx  
08. 8048496:   51                      push   %ecx  
09. 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 <puts@plt>  
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 
08048488 <main>:
 8048488: 8d 4c 24 04           lea    0x4(%esp),%ecx
 804848c: 83 e4 f0              and    $0xfffffff0,%esp
 804848f: ff 71 fc              pushl  0xfffffffc(%ecx)
 8048492: 55                    push   %ebp
 8048493: 89 e5                 mov    %esp,%ebp
 8048495: 53                    push   %ebx
 8048496: 51                    push   %ecx
 8048497: 83 ec 10              sub    $0x10,%esp
 804849a: 8b 45 04              mov    0x4(%ebp),%eax
 804849d: 89 44 24 04           mov    %eax,0x4(%esp)
 80484a1: c7 04 24 88 84 04 08  movl   $0x8048488,(%esp)
 80484a8: e8 87 ff ff ff        call   8048434 <__cyg_profile_func_enter>
 80484ad: c7 04 24 03 86 04 08  movl   $0x8048603,(%esp)
 80484b4: e8 8f fe ff ff        call   8048348 <puts@plt>
 80484b9: bb 00 00 00 00        mov    $0x0,%ebx
 80484be: 8b 45 04              mov    0x4(%ebp),%eax
 80484c1: 89 44 24 04           mov    %eax,0x4(%esp)
 80484c5: c7 04 24 88 84 04 08  movl   $0x8048488,(%esp)
 80484cc: e8 8d ff ff ff        call   804845e <__cyg_profile_func_exit>
 80484d1: 89 d8                 mov    %ebx,%eax
 80484d3: 83 c4 10              add    $0x10,%esp
 80484d6: 59                    pop    %ecx
 80484d7: 5b                    pop    %ebx
 80484d8: 5d                    pop    %ebp
 80484d9: 8d 61 fc              lea    0xfffffffc(%ecx),%esp
 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

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/iamljj/archive/2010/09/15/5886492.aspx

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值