Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / GCC 函数调用探测功能

----------------------------------------
gcc 函数调用探测功能
gcc -finstrument-functions 使用,
man gcc 是很好的帮助。
----------------------------------------收集一个函数调用的踪迹,一种方法是通过在函数的入口处和出口处插入一个打印语句来检测。这个过程非常繁琐,而且很容易出错,通常需要对源代码进行大量的修改。
幸运的是,GNU 编译器工具链(也称为 gcc)提供了一种自动检测应用程序中的各个函数的方法。
gcc 加上该选项 -finstrument-functions
在检测函数入口,会调用: void __cyg_profile_func_enter( void *func_address, void *call_site )
在检测函数出口,会调用:void __cyg_profile_func_exit ( void *func_address, void *call_site )
__cyg 开头,是Cygnus 的贡献.
在执行应用程序时,就可以收集调用者地址和被调用着地址,如果加上 -g 选项,增加调试信息,则用addr2line -f 可以找到地址对应的函数名称。
看看汇编会使你更清楚。代码不需要修改,仅通过修改编译选项,使其具有了函数调用探测功能。
为灵活期间,少量修改代码,加入对感兴趣的调用探测,这无疑是很好的想法。下面给出自己整理的一个例子.
------------------------------------------------------------
debug 代码
------------------------------------------------------------
[/pts/1@linuxidc ~/test]$ cat instrument.h
#ifndef INSTRUMENT_H
#define INSTRUMENT_H
void enable_instrument( void ) __attribute__ ((no_instrument_function));
void disable_instrument( void ) __attribute__ ((no_instrument_function));
void main_destructor( void ) __attribute__ ((no_instrument_function, destructor));
void __cyg_profile_func_enter( void *, void *) __attribute__ ((no_instrument_function));
void __cyg_profile_func_exit( void *, void *) __attribute__ ((no_instrument_function));
#endif 
[/pts/1@linuxidc ~/test]$ cat instrument.c
#include <stdio.h>
#include "instrument.h"#define INSTRUMENT_FILE_PATH "instrument.log"static FILE *instrument_fd = NULL;
static int _flag = 0;#define open_instrument_file() 
    (instrument_fd = fopen(INSTRUMENT_FILE_PATH, "w"))#define close_instrument_file() 
   do{ 
   if (NULL != instrument_fd) 
   { 
   fclose(instrument_fd); 
   instrument_fd = NULL;
   } 
   }while(0) //首次打印会打开文件
#define instrument_print(args, fmt...)
   do{ 
   if (0 == _flag)
   { 
   break; 
   } 
   if (NULL == instrument_fd && NULL == open_instrument_file()) 
   {
   printf("Err: Can not open output file. "); 
   break; 
   }
   fprintf(instrument_fd, args, ##fmt); 
       fflush(instrument_fd); 
   }while(0);//enable, disable 配对使用,在关注的函数上添加
void enable_instrument( void )
{
    _flag = 1;
}
void disable_instrument( void )
{
    _flag = 0;
}
// 一般是用不到该函数了
void main_destructor( void )
{
    close_instrument_file();
}
void __cyg_profile_func_enter( void *func_addr, void *call_site )
{
    instrument_print("Enter %p %p ", call_site, func_addr);
}
void __cyg_profile_func_exit( void *func_addr, void *call_site )
{
    instrument_print("Exit %p %p ", call_site, func_addr);
}------------------------------------------------------------
测试代码
------------------------------------------------------------
[/pts/1@linuxidc ~/test]$ cat test.c
#include <stdio.h>
#include "instrument.h"int addmul(int i, int j);
int mul(int i, int j);
int main(int argc, char *argv[])
{
    enable_instrument();
    int i=addmul(3,5);
    printf("result is %d ",i);
    disable_instrument();
    return 0;
}
int addmul(int i, int j)
{
    int k1,k2;
    k1=i+j;
    k2=mul(i,j);
    return k1+k2;
}int mul(int i, int j)
{
    return i * j;
}------------------------------------------------------------
Makefile
------------------------------------------------------------
[/pts/1@linuxidc ~/test]$ cat Makefile
CFLAGS=-finstrument-functions
all:
    gcc -c -g -finstrument-functions -o  test.o test.c
    gcc -c -g -finstrument-functions  -o  instrument.o instrument.c
    gcc -o test test.o instrument.o
    @echo ok!
clean:
    rm test------------------------------------------------------------
辅助工具
------------------------------------------------------------
[/pts/1@linuxidc ~/test]$ cat addr2line.sh
#!/bin/shif [ $# != 3 ]; then
    echo "Usage: addr2line.sh executefile addressfile functionfile"
    exit
fi;rm $3
cat $2 | while read line
      do
          if [ "$line" = "Enter" ]; then
              read line1
              read line2
              addr2line -e $1 -f $line1 -s >> $3
              echo "-----> call" >> $3
              addr2line -e $1 -f $line2 -s | sed "s/^/    /" >> $3
              echo >> $3
          elif [ "$line" = "Exit" ]; then
              read line1
              read line2
              addr2line -e $1 -f $line2 -s | sed "s/^/    /" >> $3
              echo "<----- return" >> $3
              addr2line -e $1 -f $line1 -s >> $3
              echo >> $3
          fi;
      done------------------------------------------------------------
执行过程:
1.生成执行文件
2.执行文件,生成调用记录文件->instrument.log
3.生成调用函数文件
------------------------------------------------------------
[/pts/1@linuxidc ~/test]$ make
gcc -c -g -finstrument-functions -o  test.o test.c
gcc -c -g -finstrument-functions  -o  instrument.o instrument.c
gcc -o test test.o instrument.o
ok!
[/pts/1@linuxidc ~/test]$ ./test
result is 23
[/pts/1@linuxidc ~/test]$ cat instrument.log
Enter
0x4006c6
0x400701
Enter
0x400739
0x40075c
Exit
0x400739
0x40075c
Exit
0x4006c6
0x400701[/pts/1@linuxidc ~/test]$ ./addr2line.sh test instrument.log instrument.txt
[/pts/1@linuxidc ~/test]$ cat instrument.txt
main
test.c:9
-----> call
    addmul
    test.c:17addmul
test.c:20
-----> call
    mul
    test.c:25    mul
    test.c:25
<----- return
addmul
test.c:20    addmul
    test.c:17
<----- return
main
test.c:9GCC编译器入门  http://www.linuxidc.com/Linux/2015-01/111431.htmUbuntu 12.04嵌入式交叉编译环境arm-linux-GCC搭建过程图解 http://www.linuxidc.com/Linux/2013-06/85902.htmUbuntu 12.10安装交叉编译器arm-none-linux-gnueabi-GCC http://www.linuxidc.com/Linux/2013-03/82016.htmUbuntu下Vim+GCC+GDB安装及使用 http://www.linuxidc.com/Linux/2013-01/78159.htmUbuntu下两个GCC版本切换 http://www.linuxidc.com/Linux/2012-10/72284.htmGCC 的详细介绍:请点这里
GCC 的下载地址:请点这里本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-01/111500.htm