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




  

相关话题

  全员编程的时代,我是不是应该放弃这条路? 
  你在公司项目里面看到过哪些操蛋的代码? 
  同是主流操作系统,为什么macOS没有盗版系统而Windows到处是盗版? 
  怎样才算精通SQL? 
  是否无法写一段代码将这段代码自己打印出来? 
  为什么程序语言要设计成使用这么多shift? 
  Linux内核代码大佬们如何观看的? 
  怎样才算精通SQL? 
  厉害的程序员到底用不用 IDE,如果不用,为什么? 
  如果一门编程语言中不允许对象(或结构体)循环引用,那么用它实现什么功能会比较困难? 

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





© 2024-11-25 - tinynew.org. All Rights Reserved.
© 2024-11-25 - tinynew.org. 保留所有权利