百科问答小站 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?:-)




  

相关话题

  到什么程度才叫精通 Linux? 
  为什么我们需要了解编程的历史? 
  大一学生,刚开始学习编程,但感觉自己并没有天赋怎么办? 
  为什么大多数的程序员的编程界面背景都是黑色的? 
  你是如何自学R语言的? 
  将记忆保存在电脑上,肉体坏死的人,算死亡了吗? 
  如果软件正在占领全世界,为什么程序员得不到尊重? 
  使用命令行形式的程序的时候,能不能把需要输入的命令们提前编制好,再自动执行呢? 
  为什么在计算机科学领域及编程中不使用现代数学建立的符号体系进行操作? 
  为什么 Java 总被黑? 

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





© 2024-12-26 - tinynew.org. All Rights Reserved.
© 2024-12-26 - tinynew.org. 保留所有权利