概述
Callgrind是一个分析工具,它以调用图的形式记录程序运行过程中函数之间的调用历史。 默认情况下,收集的数据包括执行的指令数量、它们与源代码行的关系、函数之间的调用者/被调用者关系以及此类调用的数量。 缓存模拟和分支预测(类似于Cachegrind)可以产生关于应用程序运行时行为的进一步信息。
概要数据在程序终止时被写入一个文件。 对于数据的表示和分析的交互控制,提供了两个命令行工具:
callgrind_annotate
这个命令读入配置文件数据,并打印一个排序的函数列表,可选地带有源注释。
callgrind_control
这个命令使您能够交互式地观察和控制当前在Callgrind控制下运行的程序的状态,而不需要停止该程序。 您可以获得统计信息以及当前堆栈跟踪,还可以请求计数器归零或转储配置文件数据。
功能
Cachegrind收集flat profile data:事件计数(数据读取、缓存丢失等)直接归因于它们发生的函数。 这种成本归因机制称为called self or exclusive attribution.
Callgrind通过跨函数调用边界传播成本来扩展此功能。 如果函数foo调用bar,来自bar的代价会被添加到foo的代价中。 当应用到整个程序时,这构建了一个所谓的包含成本的图像,也就是说,每个函数的成本包括它直接或间接调用的所有函数的成本。(Callgrind extends this functionality by propagating costs across function call boundaries. If function foo calls bar, the costs from bar are added into foo’s costs. When applied to the program as a whole, this builds up a picture of so called inclusive costs, that is, where the cost of each function includes the costs of all functions it called, directly or indirectly.)
例如,main的包含成本几乎应该是总计划成本的100%。 由于main在运行之前产生的成本,例如初始化运行时链接器和构造全局c++对象,main的包含成本并不是程序总成本的100%。
与调用图一起,这允许您找到从main开始的特定调用链,其中发生了程序的大部分成本。 调用者/被调用者成本属性对于分析从多个调用站点调用的函数,以及优化机会依赖于更改调用者中的代码(特别是通过减少调用计数)也很有用。
Callgrind的缓存模拟是基于Cachegrind的。 请先阅读Cachegrind的文档:缓存和分支预测分析器。 下面的材料描述了除Cachegrind特性外支持的特性。
Callgrind检测函数调用和返回的能力取决于运行它的平台的指令集。 它在x86和amd64上工作得最好,不幸的是目前在PowerPC、ARM、Thumb或MIPS代码上工作得不太好。 这是因为在这些指令集中没有显式的调用或返回指令,所以Callgrind必须依靠启发式方法来检测调用和返回。
基本用法
与使用Cachegrind一样,编译程序时需要增加-g选项
使用valgrind启动一个程序,请执行:
valgrind --tool=callgrind [callgrind options] your-program [program options]
当上面的模拟在进行时,你可以使用callgrind_control进行实时观察:
callgrind_control -b
This will print out the current backtrace. To annotate the backtrace with event counts, run
callgrind_control -e -b
程序终止后,会有一个名为callgrind.out.<pid>的文件生成,其中pid是被分析程序的进程ID。数据文件包含有关程序中执行的函数之间的调用的信息,以及指令读取(Ir)事件计数。
如果想从callgrind.out.<pid>文件生成分析的结果,使用下面这条命令
callgrind_annotate [options] callgrind.out.<pid>
这个分析的结果类似于使用cg_annotation运行Cachegrind得到的输出:函数列表是按函数的独占的花费排序的,也就是显示出来的那些。对于Callgrind的其他功能来说,重要的是以下两个选项:
--inclusive=yes
: Instead of using exclusive cost of functions as sorting order, use and show inclusive cost.--tree=both
: Interleave into the top level list of functions, information on the callers and the callees of each function. In these lines, which represents executed calls, the cost gives the number of events spent in the call. Indented, above each function, there is the list of callers, and below, the list of callees. The sum of events in calls to a given function (caller lines), as well as the sum of events in calls from the function (callee lines) together with the self cost, gives the total inclusive cost of the function.
默认情况下,您还将获得所有相关函数的注释源代码,这些源代码都可以找到。除了由cg_annotate生成的源注释之外,您还将看到带有调用计数的注释调用站点。对于所有其他选项,请参考(Cachegrind)文档中的cg_annotate。
为了获得更好的调用图浏览体验,强烈建议使用KCachegrind。如果代码的成本中有很大一部分是周期(以递归方式相互调用的函数集),则必须使用KCachegrind,因为callgrind_annotate目前不执行任何周期检测,这对于在本例中获得正确的结果非常重要。
如果您还对测量程序的缓存行为感兴趣,可以使用Callgrind和选项——cache-sim=yes。对于分支预测模拟,使用——branch-sim=yes。预计将进一步放缓约2倍。
如果您想要分析的程序部分位于运行过程中的某个位置,那么不进行任何分析就快进到这个部分,然后启用分析是有好处的。This is achieved by using the command line option --instr-atstart=no and running, in a shell: callgrind_control -i on just before the interesting code section is executed. 要精确地指定应该开始分析的代码位置,可以使用客户机请求CALLGRIND_START_INSTRUMENTATION。
如果您希望能够看到程序集代码级别的注释,请指定–dump-instr=yes。这将在指令粒度上生成概要数据。注意,生成的概要数据只能通过KCachegrind查看。对于程序集注释,了解函数内部控制流的更多细节也很有趣,即(条件)跳转。This will be collected by further specifying --collect-jumps=yes.