百科问答小站 logo
百科问答小站 font logo



如何快速地在每个函数入口处加入相同的语句? 第1页

  

user avatar   lan-se-52-30 网友的相关建议: 
      

若你熟悉Clang,你可以利用Clang来做source-to-source转换,也就是去访问每一个函数的定义,然后插入你的代码。

但是,如

@陈硕

所言,对于function trace,我们不需要这么麻烦,因为编译器其实有帮你做这件事情,也就是-finstrument-functions。有了这个编译选项后,编译器会在每一个函数开始处插入__cyg_profile_func_enter,退出的地方插入__cyg_profile_func_exit,你所需要做的就是实现这两个函数,而不用动代码的其它地方。

我举一个简单的例子,首先我们有用于跟踪函数的func_trace.c

       #include <stdio.h>  static FILE *fp_trace;  void __attribute__((constructor)) traceBegin(void) {   fp_trace = fopen("func_trace.out", "w"); }  void __attribute__((destructor)) traceEnd(void) {   if (fp_trace != NULL) {     fclose(fp_trace);   } }  void __cyg_profile_func_enter(void *func, void *caller) {   if (fp_trace != NULL) {     fprintf(fp_trace, "entry %p %p
", func, caller);   } }  void __cyg_profile_func_exit(void *func, void *caller) {   if (fp_trace != NULL) {     fprintf(fp_trace, "exit %p %p
", func, caller);   } }      

使用 gcc func_trace.c -c 产生目标文件

随后我们编写一个简单的测试代码main.c

       #include <stdio.h>  int foo(void) {     return 2;  }  int bar(void) {   zoo();   return 1; }  void zoo(void) {    foo();  }  int main(int argc, char **argv) {    bar();  }      

随后将main.c与func_trace.o一起编译,并且记得加上-finstrument-functions, 即:

gcc main.c func_trace.o -finstrument-functions

然后运行./a.out,就会产生func_trace.out,里面的文件内容会类似这样:

entry 0x4006d6 0x7f60c11a7ec5

entry 0x400666 0x4006fb

entry 0x4006a9 0x40068a

entry 0x40062d 0x4006c3

exit 0x40062d 0x4006c3

exit 0x4006a9 0x40068a

exit 0x400666 0x4006fb

exit 0x4006d6 0x7f60c11a7ec5

那么接下来就是处理这个跟踪文件的数据了,你可以利用addr2line达到这一点: addr2line -f -e $EXE $ADDR 即可以显示出来函数名字了,所以我们可以写一个小的Shell脚本来达到目的:

       #!/bin/bash EXECUTABLE="$1" TRACELOG="$2" while read TRACEFLAG FADDR CADDR; do FNAME="$(addr2line -f -e ${EXECUTABLE} ${FADDR}|head -1)" if test "${TRACEFLAG}" = "entry" then CNAME="$(addr2line -f -e ${EXECUTABLE} ${CADDR}|head -1)" CLINE="$(addr2line -s -e ${EXECUTABLE} ${CADDR})" echo "Enter ${FNAME} called from ${CNAME} (${CLINE})" fi if test "${TRACEFLAG}" = "exit" then echo "Exit  ${FNAME}" fi done < "${TRACELOG}"      

然后使用方法很简单 ./func_trace.sh a.out func_trace.out


就会出现函数的调用关系,而 main called from ?? 的 ?? 是 C 库的东西。而后面的 ?? : ? 则是代表没有debug信息,如果你想查看的话,那么你需要编译的时候加上 -g,如下所示:

其实,若有了这样的数据,可以继续往下做下去,如可以利用graphviz建立可视化调用关系图,其实就是 IDE 的函数调用关系图,你不如考虑直接使用IDE?:-)




  

相关话题

  你在 GitHub 上看到过哪些有意思的 Issue? 
  计算机硕士不会编程怎么找工作? 
  在计算机中utility应该怎么翻译? 
  为什么不把push ebp和mov ebp, esp的操作通过硬件方式做进call指令中? 
  如何看待奥巴马呼吁每个美国人都学习编程? 
  如何用python读取下面的csv文件? 
  C#为什么非要把函数叫方法? 
  为什么C语言中计算机认为0是假的,其他数都是真的? 
  C++ 实现接口与实现分离后,文件变得更多了,到底有什么好处? 
  用JavaScript做其他语言擅长的事情“好”么? 

前一个讨论
为什么中国一年可以出一个暴发户,却十年培养不出一个有贵族精神的人?
下一个讨论
如何让被女德班洗脑的长辈意识到,女德班教的国学都是文化糟粕?





© 2024-05-20 - tinynew.org. All Rights Reserved.
© 2024-05-20 - tinynew.org. 保留所有权利