Perf中的怪异回溯

问题描述 投票:0回答:1

我使用以下命令在简单的L3-misses基准中提取导致用户级别evince的回溯:

sudo perf record -d --call-graph dwarf -c 10000 -e mem_load_uops_retired.l3_miss:uppp /opt/evince-3.28.4/bin/evince

很明显,采样周期很大(连续采样之间发生10000个事件)。对于此实验,perf script的输出具有与此示例相似的一些示例:

EvJobScheduler 27529 26441.375932:      10000 mem_load_uops_retired.l3_miss:uppp:     7fffcd5d8ec0         5080022 N/A|SNP N/A|TLB N/A|LCK N/A
    7ffff17bec7f bits_image_fetch_separable_convolution_affine+0x2df (inlined)
    7ffff17bec7f bits_image_fetch_separable_convolution_affine_pad_x8r8g8b8+0x2df (/usr/lib/x86_64-linux-gnu/libpixman-1.so.0.34.0)
    7ffff17d1fd1 general_composite_rect+0x301 (/usr/lib/x86_64-linux-gnu/libpixman-1.so.0.34.0)
  ffffffffffffffff [unknown] ([unknown])

在回溯的底部,有一个名为[unknown]的符号,看起来不错。但是随后会调用general_composite_rect()中的一行。这个回溯行吗?

AFAIK,回溯中的第一个调用方应类似于_start()__GI___clone()。但是回溯不是这种形式。怎么了?

有什么方法可以解决此问题?截断的回溯部分可靠吗?

linux trace performancecounter perf call-graph
1个回答
0
投票
如果堆栈中没有保存帧指针,也没有矮人方法的CFI表,则

TL; DR perf回溯过程可能会在某些功能处停止。用-fno-omit-frame-pointer-g重新编译库或获取debuginfo。使用发行版的二进制文件和库文件,perf通常会尽早停止回溯,而没有机会达到main()_startclone()/start_thread()顶级功能。

perf]分析工具是Linux中的统计采样探查器(无二进制工具):它对软件计时器或事件源或硬件性能监视单元(PMU)进行编程,以生成定期中断。在你的例子中-c 10000 -e mem_load_uops_retired.l3_miss:uppp用于以某种PEBS模式(https://easyperf.net/blog/2018/06/08/Advanced-profiling-topics-PEBS-and-LBR)在x86_64中选择硬件PMU,以在10000 mem_load_uops_retired(带有l3_miss掩码)后生成中断。生成的中断由Linux内核(perf_events subsystem,内核/事件和arch/x86/events)处理。在该处理程序中,PMU被复位(重新编程)以在10000个以上的事件和样本产生之后产生下一个中断。通过perf report命令将样品数据转储保存到perf.data文件中,但是每次使用此工具后,都可以保存数千个样品。可以通过perf scriptperf script -D读取样本。

perf_events中断处理程序,接近kernel / events / core.c的__perf_event_overflow,可以完全访问当前函数的寄存器,并且有一些时间可以进行其他数据检索以记录当前时间,pid等。这样的过程是__perf_event_overflow数据收集。但是使用x86_64和-fomit-frame-pointer(通常为Debian / Ubuntu / others的许多系统库启用)时,在寄存器或函数堆栈中没有默认位置可存储框架指针:

https://en.wikipedia.org/wiki/Call_stack

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Optimize-Options.html#index-fomit_002dframe_002dpointer-692不要将帧指针保存在不需要一个功能的寄存器中。这样可以避免有关保存,设置和恢复帧指针;它还在以下位置提供了额外的寄存器许多功能。这也使某些机器上的调试变得不可能。

从GCC 4.6版开始,是32位Linux x86和32位Darwin x86的默认设置(当未针对大小进行优化时)目标已更改为-fomit-frame-pointer。默认可以是通过使用以下命令配置GCC,将其恢复为-fno-omit-frame-pointer--enable-frame-pointer配置选项。

通过将帧指针保存在函数堆栈中,回溯/展开很容易。但是对于某些功能,现代gcc(和其他编译器)可能不会生成帧指针。因此,像在perf_events处理程序中那样的回溯代码将在该函数处停止回溯,或者需要另一种帧指针恢复方法。 -fomit-frame-pointer的选项-g method--call-graph)选择要使用的方法。它记录在perf record man perf-record

http://man7.org/linux/man-pages/man1/perf-record.1.html设置并启用调用图(堆栈链/回溯)记录,暗含-g。默认值为“ fp”。

允许指定“ fp”(帧指针)或“矮人”(DWARF的CFI-呼叫帧信息)或“ lbr”(硬件最后分支记录)设施)作为收集用于显示调用图。

在某些系统中,使用gcc构建二进制文件--fomit-frame-pointer,使用“ fp”方法,将使用“矮人”(如果可用)生成伪造的调用图(链接到应该使用libunwind或libdw库)。使用“ lbr”方法不需要任何编译器选项。它将产生呼叫硬件LBR寄存器中的图形。主要限制是它仅在新的Intel平台(例如Haswell)上可用。它只能获得用户呼叫链。它不适用于分支堆栈同时采样。

[使用“矮人”记录时,perf还会记录(用户)堆栈转储采样时。堆栈转储的默认大小为8192(字节)。用户可以通过在逗号后传递大小来更改大小“ --call-graph dwarf,4096”。

因此,矮化方法重用CFI表来查找堆栈帧大小并查找调用方的堆栈帧。我不确定默认情况下是否从发布库中删除了CFI表。但是debuginfo可能会有它们。 LBR没有帮助,因为它的硬件缓冲区很短。矮拆分处理(内核处理程序保存了堆栈的一部分,而perf用户空间工具将使用libdw + libunwind对其进行解析)可能会丢失调用堆栈的某些部分,因此也请尝试使用--call-graph--call-graph dwarf,10240等来增加矮堆栈的转储。 。

回溯在perf_events的与拱有关的部分中实现:--call-graph dwarf,81920;从arch/x86/events/core.c:perf_callchain_user()perf_callchain_user()kernel/events/callchain.c:get_perf_callchain()get_perf_callchain()perf_prepare_sample__perf_event_output调用。

Gregg确实警告了perf的调用栈不完整:*(event->overflow_handler)

不完整的堆栈通常意味着使用了-fomit-frame-pointer –编译器优化,在现实世界中几乎没有什么积极的变化,但是破坏了堆栈分析器。始终使用-fno-omit-frame-pointer进行编译。最新的perf具有-g dwarf选项,以使用备用libunwind / dwarf方法来检索堆栈。

我也通过一些其他链接写过关于perf中的回溯的信息:*(event->overflow_handler)

© www.soinside.com 2019 - 2024. All rights reserved.