gcc –finstrument-functions特性的应用

1          gcc –finstrument-functions特性的应用

      这几天看了一下nginx的源码,分析过程中发现nginx模块加载、初始化这部分利用了函数指针的方式挂在的,通过阅读源码的方式寻找函数调用关系比较吃力。所以想起了通过gcc的–finstrument-functions特性,打印出函数调用栈。参照这个调用栈,再来分析代码,容易多了。gcc这个特性最大的魅力就是不需要修改源代码,只需要重新编译、连接。即可。

1.1        gcc特性介绍

1.1.1      -finstrument-functions

Generateinstrumentation calls for entry and exit to functions. Just after functionentry and just before function exit, the following profiling functions will becalled with the address of the current function and its call site. (On someplatforms, __builtin_return_address does not work beyond the current function, so the call site informationmay not be available to the profiling functions otherwise.)

         void __cyg_profile_func_enter (void *this_fn,

                                         void*call_site);

         void __cyg_profile_func_exit (void *this_fn,

                                         void*call_site);

    

The first argumentis the address of the start of the current function, which may be looked upexactly in the symbol table.

Thisinstrumentation is also done for functions expanded inline in other functions.The profiling calls will indicate where, conceptually, the inline function isentered and exited. This means that addressable versions of such functions mustbe available. If all your uses of a function are expanded inline, this may meanan additional expansion of code size. If you use `extern inline' in your C code,an addressable version of such functions must be provided. (This is normallythe case anyways, but if you get lucky and the optimizer always expands thefunctions inline, you might have gotten away without providing static copies.)

A function may begiven the attribute no_instrument_function, in which casethis instrumentation will not be done. This can be used, for example, for theprofiling functions listed above, high-priority interrupt routines, and anyfunctions from which the profiling functions cannot safely be called (perhapssignal handlers, if the profiling routines generate output or allocate memory). 

   贴出了gcc官方文档的说明,以便参考。主要意思是:

1、如果我们在编译、连接的时候,如果给gcc增加了 -finstrument-functions选项,那么,gcc会自动在函数入口的地方添加__cyg_profile_func_enter函数的调用,并且把函数的地址通过this_fn参数来传递;在函数出口的地方,添加__cyg_profile_func_exit函数的调用,同样通过this_fn参数来传递当前函数的地址。这样一来,我们可以通过实现__cyg_profile_func_enter和__cyg_profile_func_exit函数,保存函数的地址,记录我们函数调用栈的信息。

2、内联函数,也会在代码扩展的地方分别添加__cyg_profile_func_enter和__cyg_profile_func_exit函数的调用。

3、不需要gcc插入__cyg_profile_func_enter和__cyg_profile_func_exit的函数,在声明的时候增加no_instrument_function属性的说明。

例如,

void f1() __attribute__ ((no_instrument_function));

 

1.1.2      -finstrument-functions-exclude-file-list=file,file,...

Set the list of functions that areexcluded from instrumentation (see the description of -finstrument-functions). If the file that contains a functiondefinition matches with one of file, then thatfunction is not instrumented. The match is done on substrings: if the file parameter is a substring of the file name,it is considered to be a match.

For example, -finstrument-functions-exclude-file-list=/bits/stl,include/sys will exclude any inline function defined in files whose pathnames contain /bits/stl orinclude/sys.

If, for somereason, you want to include letter ',' in one of sym, write '\,'. For example, -finstrument-functions-exclude-file-list='\,\,tmp' (note the single quote surrounding the option). 

这个特性是将不需要使用instrument-functions机制的文件排除掉。

1.1.3      -finstrument-functions-exclude-function-list=sym,sym,...

This is similar to -finstrument-functions-exclude-file-list, but this optionsets the list of function names to be excluded from instrumentation. Thefunction name to be matched is its user-visible name, such as vector blah(const vector &), not the internalmangled name (e.g., _Z4blahRSt6vectorIiSaIiEE). The match isdone on substrings: if the sym parameter is asubstring of the function name, it is considered to be a match.

这个特性是将不需要使用instrument-functions机制的函数排除掉。

1.1.4 constructor&& destructor

constructor

destructor

The constructor attribute causesthe function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () has completed or exit () has been called.Functions with these attributes are useful for initializing data that will beused implicitly during the execution of the program.

 gcc这个属性,上面官方解释很清楚。我们就是要利用这个特性,在main函数执行前,打开一个文件,保存函数调用信息,在main函数返回后,关闭这个文件。

1.2        事例

主要思路是,把函数的地址保存在一个文件中,然后通过gnu bintools工具里面的addr2line将函数地址,转换成函数名称和代码行。所以,我们需要做以下工作:

1、 实现__cyg_profile_func_enter和__cyg_profile_func_exit函数

2、  提供constructor && destructor函数,在main函数执行前,打开一个文件,保存函数调用信息,在main函数返回后,关闭这个文件。这里取用的文件名是trace.txt,

生成的文件格式:“enter|leave:函数地址” , 例如,

 enter:0x804b483

 enter:0x80692dc

 leave:0x80692dc

 enter:0x804b483

 leave:0x804b483

 enter:0x8056f1b

 enter:0x8056b59

 enter:0x80566f1

 leave:0x80566f1

3、  调用addr2line 工具解析函数地址(我提供了一个python解析工具)

解析后的格式:”enter | leave<函数名称> 函数源码路径”,例如,

enter

/root/source/nginx-1.2.0/src/core/nginx.c:201

enter/root/source/nginx-1.2.0/src/os/unix/ngx_errno.c:47

leave /root/source/nginx-1.2.0/src/os/unix/ngx_errno.c:47

enter /root/source/nginx-1.2.0/src/core/nginx.c:201

leave /root/source/nginx-1.2.0/src/core/nginx.c:201

 

注意事项:

1、  编译的时候需要增加 –g选项,生成符号信息

2、  编译的时候增加-finstrument-functions选项,开启这个机制

3、  上面提供的所有分析函数,声明的时候必须增加

__attribute__((no_instrument_function));说明,否则会出现函数调用死循环。

1.2.1 源代码

  1 #include

  2#include

  3

  4static FILE *fp;

  5int g_count;

  6//#define DUMP(desp , func, call) \

  7//           fprintf(fp ,"%s:%p:%p\n", desp , func , __builtin_return_address(1))

  8void __attribute__((__no_instrument_function__))

  9dump(char * desp , void *func)

 10 {

 11    fprintf(fp , "%d %s:%p\n",g_count++ , desp , func );

 12    fflush(fp);

 13 }

 14/* Constructor and Destructor Prototypes */

 15void main_constructor( void )

 16    __attribute__ ((no_instrument_function, constructor));

 17void main_destructor( void )

 18    __attribute__ ((no_instrument_function, destructor));

 19    /* Output trace file pointer */

 20

 21    void

 22main_constructor( void )

 23 {

 24    fp = fopen( "trace.txt", "w" );

 25    if (fp == NULL) exit(-1);

 26    g_count = 0;

 27 }

28     void

 29main_deconstructor( void )

 30 {

 31    fclose( fp );

 32 }

 33

 34    void __attribute__((__no_instrument_function__))

 35__cyg_profile_func_enter(void *this_func, void *call_site)

 36 {

 37    dump("enter", this_func);

 38 }

 39    void __attribute__((__no_instrument_function__))

 40__cyg_profile_func_exit(void *this_func, void *call_site)

 41 {

 42    dump("leave", this_func);

 43 }

 44

1.2.2 解析工具

  1 #!/usr/bin/python

  2

  3import sys

  4import subprocess

  5

  6fd = open(sys.argv[1] , 'r')

  7fun_name=''

  8for line in fd:

 9        fields=line.split(":",2)

 10        args1 = ['/usr/bin/addr2line', '-e', sys.argv[2] , fields[1] , '-f']

 11        

 12        handle1 = subprocess.Popen(args1, stdout=subprocess.PIPE)

 13        r1 = handle1.stdout.readlines()

 14        

 15        if(fields[0].rfind('enter') != -1):

 16            fun_name += r1[0].replace('\n' , '')

 17            print fields[0] + " <" + fun_name  + "> " + r1[1],

 18            fun_name +='##'

 19        elif(fields[0].rfind('leave') != -1):

 20            fun_name = fun_name.rstrip('##')

 21            print fields[0] + " <" + fun_name  + "> " + r1[1],

 22            fun_name = fun_name.rstrip(r1[0].replace('\n' ,''))

 23        else:

 24            print fields[0]

 25            

 26fd.close()  

你可能感兴趣的:(linux)