【问题标题】:Profiling my program with linux perf and different call graph modes gives different results使用 linux perf 和不同的调用图模式分析我的程序会给出不同的结果
【发布时间】:2019-12-12 15:07:18
【问题描述】:

我想用 linux perf 分析我的 c++ 程序。 为此,我使用了以下三个命令,但我不明白为什么会得到三个完全不同的报告。

perf record --call-graph dwarf ./myProg
perf report

perf record --call-graph fp ./myProg
perf report

perf record --call-graph lbr ./myProg
perf report

我也不明白为什么main 函数不是列表中最高的函数。

我的程序的逻辑如下,main函数调用getPogDocumentFromFile函数调用fromPoxml调用toPred调用applySubst调用subst。此外toPredapplySubstsubst 是递归函数。我希望他们成为瓶颈。

更多 cmets:我的程序运行大约 25 分钟,它是高度递归的并且分配了很多 (~17Go) 内存。我也用-fno-omit-frame-pointer 编译并使用最近的英特尔CPU。

有什么想法吗?

编辑:

再次思考我的问题,我意识到我不明白儿童专栏的含义。

到目前为止,我假设 Self 列是我们在调用堆栈顶部查看的函数的样本百分比,而 Children 列是调用堆栈中任何位置的函数的样本百分比。显然情况并非如此,否则 main 函数的子列距离 100% 不远。也许调用堆栈被截断?还是我完全误解了分析器的工作原理?

【问题讨论】:

  • 几乎不可能给出一个具体的答案,除非你能煮出一个minimal reproducible example。快速浏览一下,对于所有调用图技术跟踪的函数,不同的配置文件并没有本质上的不同——它们只是包含一组不同的覆盖函数。当调用图不完整时,main 可能太低了。原因很多。
  • 您能详细说明一下吗?为什么调用图经常不完整?我觉得如果调用图不完整,这些报告或多或少没用。
  • 需要 25 分钟?试试this。如果您有统计倾向,则解释其工作的统计原因here
  • @rsaill,如果您认为某些调用堆栈收集方法会给您截断输出,那么您应该尝试 "--no-children" option of perf report: "disable ... Accumulate callchain子项到父项,以便随后可以显示在输出中。输出将有一个新的“子项”列,并将根据数据进行排序。它需要记录调用链“。当性能报告用于不完整的调用堆栈时,有时我会得到不正确的结果。 self 列是 f 本身的样本,children - f + 从它调用的所有函数。

标签: c++ profiling perf dwarf


【解决方案1】:

Man page of pref report 记录调用链显示的子集:

  --children
       Accumulate callchain of children to parent entry so that then can
       show up in the output. The output will have a new "Children"
       column and will be sorted on the data. It requires callchains are
       recorded. See the ‘overhead calculation’ section for more
       details. Enabled by default, disable with --no-children.

我可以建议您尝试使用--no-children 选项perf report(或perf top-g --no-children -p $PID_OF_PROGRAM)的非默认模式

所以在默认模式下,当 perf.data 文件中有一些调用链数据时,perf 报告会计算“self”和“self+children”开销并对累积的数据进行排序。这意味着如果某个函数 f1() 有 10% 的“自我”样本并调用一些叶函数 f2() 有 20% 的“自我”样本,那么 f1() self+children 将是 30%。累积的数据适用于所有提到当前函数的堆栈:在它本身完成的工作,以及在所有直接和间接子代(后代)中的工作。

您可以在--call-graph 选项(dwarf / lbr / fp)中指定一些调用堆栈采样方法,它们可能会有一些限制。有时方法(尤其是 fp)可能无法提取调用堆栈的一部分。 -fno-omit-frame-pointer 选项可能会有所帮助,但是当它在您的可执行文件中使用但不在带有回调的某些库中时,调用堆栈将被部分提取。一些很长的调用链也可能被一些方法提取不出来。或者perf report可能无法处理某些情况。

要检查截断的调用链示例,请在中间的某处使用perf script|less。在这种模式下,它会打印每个记录的样本以及所有检测到的函数名称,检查不以 main__libc_start_main 结尾的样本 - 它们被截断。

否则主函数的子列将不会超过 100%

是的,对于单线程程序和正确记录和处理的调用堆栈,main 在“儿童”列中应该有 99% 左右。对于多线程程序,第二个线程和其他线程将有另一个根节点,例如 start_thread。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-24
    • 2014-04-08
    • 2021-06-05
    • 1970-01-01
    • 2020-08-01
    • 2020-08-21
    • 1970-01-01
    相关资源
    最近更新 更多