有没有一种方法可以找出一个C函数的顶级调用者?

5
我有一个函数被许多不同的地方频繁调用,我想找出谁调用这个函数最多。例如,前5个调用者或任何调用此函数超过N次的人。
我正在使用AS3 Linux,gcc 3.4。
目前,我只是设置了一个断点,然后在每300次之后停止,因此是暴力解决方法...
有人知道可以帮助我的工具吗?
谢谢

我也没有,但我觉得我从来没有遇到过一个不能做到这一点的分析工具。 - Michael Myers
1
oprofile无法告诉我们哪个函数调用最频繁... - vehomzzz
7个回答

18

使用 -pg 选项编译程序,运行一段时间后使用 gprof 分析。使用 -pg 选项编译的程序将生成带有执行概要信息的 gmon.out 文件。gprof 可以读取该文件并以可读格式展示。


3
而且 gprof2dot(http://code.google.com/p/jrfonseca/wiki/Gprof2Dot)可以将输出变成漂亮的图表 ;-) - ChristopheD

2

我为了好玩写了一个通话记录示例。宏将函数调用更改为带有仪器的调用。

include <stdio.h>. 

int funcA( int a, int b ){ return a+b; }

// instrumentation

void call_log(const char*file,const char*function,const int line,const char*args){
  printf("file:%s line: %i function: %s args: %s\n",file,line,function,args);
}

#define funcA(...) \ 
  (call_log(__FILE__, __FUNCTION__, __LINE__, "" #__VA_ARGS__), funcA(__VA_ARGS__)). 

// testing

void funcB(void){
  funcA(7,8);
}


int main(void){
  int x = funcA(1,2)+

          funcA(3,4);

  printf( "x: %i (==10)\n", x );

  funcA(5,6);

  funcB();
}

输出:

file:main.c line: 22 function: main args: 1,2
file:main.c line: 24 function: main args: 3,4
x: 10 (==10)
file:main.c line: 28 function: main args: 5,6
file:main.c line: 17 function: funcB args: 7,8

2

性能分析有助于提升应用程序的效率。


我正在使用OProfile工具,但它没有提供这个级别的信息。 - vehomzzz
有很多人这样做。Danadam在他的回答中提出了一个建议。 - Georg Schölly

1
一个有些繁琐但不需要额外工具的方法是:
#define COUNTED_CALL( fn, ...) do{ \
    fprintf( call_log_fp, "%s->%s\n", __FUNCTION__, #fn ) ; \
    (fn)(__VA_ARGS__) ; \
}while(0) ;

然后所有的调用都写成:

int input_available = COUNTED_CALL( scanf, "%s", &instring ) ;

将被记录到与call_log_fp相关联的文件中(您必须已经初始化全局FILE*)。上述日志将如下所示:

main->scanf

然后,您可以处理该日志文件以提取所需的数据。您甚至可以编写自己的代码来进行仪器化,这可能会使其不那么繁琐。

但对于C++类成员函数来说可能有点模糊。我不确定是否存在__CLASS__宏。


1

既然您在另一条评论中提到了oprofile,我想说oprofile支持在被分析的程序上生成调用图(callgraphs)。

请参阅http://oprofile.sourceforge.net/doc/opreport.html#opreport-callgraph以获取更多详细信息。

值得注意的是,这肯定不像gprof或其他性能分析器所提供的调用者分析(profile)那样清晰,因为它报告的数字是oprofile在一个给定函数中收集到X作为调用者的样本数,而不是X调用给定函数的次数。但这应该足以找出给定函数的顶级调用者。


0
除了上述的gprof分析工具外,你还可以尝试使用gcov代码覆盖率工具。关于如何编译和使用这两个工具的信息应该包含在gcc手册中。

0

再次提醒,堆栈采样来拯救!只需获取一堆“堆栈快照”,数量随意。丢弃任何不包含您的函数(称其为F)的样本。(如果您要丢弃大部分样本,则F不是性能问题。)

在每个剩余的样本中,找到对F的调用,并查看该调用所在的函数(称其为G)。如果F是递归的(在样本中出现多次),则仅使用最顶层的调用。

按每个G出现在多少个堆栈中进行排名。

如果您不想手动执行此操作,可以制作一个简单的工具或脚本。您不需要数以万计的样本。20个左右的样本将为您提供相当好的信息。

顺便说一下,如果您真正想做的是找到性能问题,实际上您并不需要执行所有那些丢弃和排名操作。事实上-不要丢弃每个G内部的调用指令的确切位置。这些实际上可以告诉您比它们仅仅在G内部某处更多的信息。

附言:这一切都基于你说“最多”时的假设,意思是“在调用它时花费了最多的挂钟时间”,而不是“调用次数最多”。如果你关心性能,挂钟时间的分数比调用次数更有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接