----------------------------------------
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.\n"); \
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\n%p\n%p\n", call_site, func_addr);
}
void __cyg_profile_func_exit( void *func_addr, void *call_site )
{
instrument_print("Exit\n%p\n%p\n", 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\n",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/sh
if [ $# != 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:17
addmul
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:9
GCC編譯器入門 http://www.linuxidc.com/Linux/2015-01/111431.htm
Ubuntu 12.04嵌入式交叉編譯環境arm-linux-GCC搭建過程圖解 http://www.linuxidc.com/Linux/2013-06/85902.htm
Ubuntu 12.10安裝交叉編譯器arm-none-linux-gnueabi-GCC http://www.linuxidc.com/Linux/2013-03/82016.htm
Ubuntu下Vim+GCC+GDB安裝及使用 http://www.linuxidc.com/Linux/2013-01/78159.htm
Ubuntu下兩個GCC版本切換 http://www.linuxidc.com/Linux/2012-10/72284.htm
GCC 的詳細介紹:請點這裡
GCC 的下載地址:請點這裡