为了分析程序,有时候需要打印出执行过程中的函数名称。
一种方案是修改程序源码,在每个函数头部添加打印函数,这样在函数较少的情况下,可以使用。这种方法,对于较大的程序就不实用。
方案1: 在每个函数中加入
printf("%s", __FUNCTION__);
方案2: 使用 -finstrument-functions
第二种方案是使用使用gcc 指令 -finstrument-functions, 添加这种指令,可以在进入程序和退出程序前,添加钩子函数。这两个函数的原型如下:
void __cyg_profile_func_enter (void *this_fn,
void *call_site);
void __cyg_profile_func_exit (void *this_fn,
void *call_site);
通过重定义这两个函数可以实现在函数进入和退出前的一些自定义操作。
示例
// file main.c
int square(int x) { return x*x; }
int main()
{
square(2);
return 0;
}
如下编写自定义函数,本例中将其命名为 instrument.c
// file instrument.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
void __cyg_profile_func_enter (void *, void *)
__attribute__((no_instrument_function));
void __cyg_profile_func_enter (void *this_fn, void *call_site)
{
Dl_info info;
if (dladdr(this_fn, &info))
fprintf (stderr, "%p [%s] %s\n",
this_fn,
info.dli_fname ? info.dli_fname : "?",
info.dli_sname ? info.dli_sname : "?");
}
编译指令
$ gcc main.c instrument.c -rdynamic -finstrument-functions -ldl
$ ./a.out
0x557119f621f0 [./a.out] main
0x557119f621a9 [./a.out] square
查看汇编
汇编如下
00000000000011f0 <main>:
11f0: f3 0f 1e fa endbr64
11f4: 55 push %rbp
11f5: 48 89 e5 mov %rsp,%rbp
11f8: 53 push %rbx
11f9: 48 83 ec 08 sub $0x8,%rsp
11fd: 48 8b 45 08 mov 0x8(%rbp),%rax
1201: 48 89 c6 mov %rax,%rsi
1204: 48 8d 3d e5 ff ff ff lea -0x1b(%rip),%rdi # 11f0 <main>
120b: e8 2b 00 00 00 callq 123b <__cyg_profile_func_enter>
1210: bf 02 00 00 00 mov $0x2,%edi
1215: e8 8f ff ff ff callq 11a9 <square>
121a: bb 00 00 00 00 mov $0x0,%ebx
121f: 48 8b 45 08 mov 0x8(%rbp),%rax
1223: 48 89 c6 mov %rax,%rsi
1226: 48 8d 3d c3 ff ff ff lea -0x3d(%rip),%rdi # 11f0 <main>
122d: e8 7e fe ff ff callq 10b0 <__cyg_profile_func_exit@plt>
1232: 89 d8 mov %ebx,%eax
1234: 48 83 c4 08 add $0x8,%rsp
1238: 5b pop %rbx
1239: 5d pop %rbp
123a: c3 retq
可以看出在进入 square 函数前,插入了callq 123b <__cyg_profile_func_enter>
,在退出 square 前,插入了 callq 10b0 <__cyg_profile_func_exit@plt>
。
参考: