linux的perf实用程序如何理解堆栈跟踪?

时间:2016-07-09 01:43:42

标签: linux-kernel perf flamegraph

Brendan Gregg使用Linux的perf实用程序为c / c ++,jvm代码,nodejs代码等生成火焰图。

Linux内核本身是否了解堆栈跟踪?在哪里可以阅读更多关于工具如何能够反省到流程的堆栈跟踪,即使流程是用完全不同的语言编写的?

1 个答案:

答案 0 :(得分:25)

Gregg在perf中简要介绍了堆栈跟踪: http://www.brendangregg.com/perf.html

  

4.4堆栈跟踪

     

始终使用帧指针进行编译。省略框架指针是一种破坏调试器的恶意编译器优化,遗憾的是,它通常是默认的。如果没有它们,您可能会看到perf_events中的不完整堆栈...有两种方法可以解决此问题:使用dwarf数据来展开堆栈,或者返回帧指针。

     

     

由于关于3.9内核,perf_events支持在用户级堆栈中丢失帧指针的解决方法:libunwind,它使用dwarf。这可以使用“-g dwarf”启用。   ...编译器优化(-O2),在这种情况下省略了帧指针。 ...使用-fno-omit-frame-pointer重新编译..

非C风格的语言可能有不同的帧格式,或者也可能省略帧指针:

  

4.3。 JIT符号(Java,Node.js)

     

具有虚拟机(VM)的程序(如Java的JVM和节点的v8)执行自己的虚拟处理器,该处理器具有执行功能和管理堆栈的方式。如果使用perf_events对这些进行分析,您将看到VM引擎的符号.perf_events有JIT支持来解决此问题,这需要VM维护/tmp/perf-PID.map文件以进行符号转换。

     

请注意,Java可能不会显示完整的堆栈,因为x86上的热点省略了帧指针(就像gcc一样)。在较新的版本(JDK 8u60 +)上,您可以使用-XX:+PreserveFramePointer选项来解决此问题,...

Gregg关于Java和堆栈跟踪的博客文章: http://techblog.netflix.com/2015/07/java-in-flames.html(“修复框架指针” - 通过在程序启动时添加选项在某些JDK8版本和JDK9中修复)

现在,您的问题:

  

linux的perf实用程序如何理解堆栈跟踪?

perf 实用程序基本上(在早期版本中)只解析从linux内核子系统“perf_events”(或有时“events”)返回的数据,访问使用系统调用perf_event_open。对于调用堆栈跟踪,有选项PERF_SAMPLE_CALLCHAIN / PERF_SAMPLE_STACK_USER

sample_type               PERF_SAMPLE_CALLCHAIN                      记录callchain(堆栈回溯)。

          PERF_SAMPLE_STACK_USER (since Linux 3.7)
                 Records the user level stack, allowing stack unwinding.
  

Linux内核本身是否了解堆栈跟踪?

它可能理解(如果实现),也可能不理解,具体取决于您的cpu架构。采样(从实时进程获取/读取调用堆栈)调用链的功能在内核的与架构无关的部分中定义为__weak,具有空体:

http://lxr.free-electrons.com/source/kernel/events/callchain.c?v=4.4#L26

 27 __weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
 28                                   struct pt_regs *regs)
 29 {
 30 }
 31 
 32 __weak void perf_callchain_user(struct perf_callchain_entry *entry,
 33                                 struct pt_regs *regs)
 34 {
 35 }

在4.4内核用户空间中,callchain采样器在内核的体系结构相关部分中重新定义,适用于x86 / x86_64,ARC,SPARC,ARM / ARM64,Xtensa,Tilera TILE,PowerPC,Imagination Meta:

http://lxr.free-electrons.com/ident?v=4.4;i=perf_callchain_user

arch/x86/kernel/cpu/perf_event.c, line 2279
arch/arc/kernel/perf_event.c, line 72
arch/sparc/kernel/perf_event.c, line 1829
arch/arm/kernel/perf_callchain.c, line 62
arch/xtensa/kernel/perf_event.c, line 339
arch/tile/kernel/perf_event.c, line 995
arch/arm64/kernel/perf_callchain.c, line 109
arch/powerpc/perf/callchain.c, line 490
arch/metag/kernel/perf_callchain.c, line 59

对于某些体系结构和/或某些模式,从用户堆栈读取调用链可能并非易事。

您使用的CPU架构是什么?使用了哪些语言和VM?

  

在哪里可以阅读更多关于工具如何能够反省到流程的堆栈跟踪的内容,即使流程是用完全不同的语言编写的?

您可以尝试gdb和/或调试器获取libc的语言或backtrace function或者支持libunwind中的只读展开(有local backtrace example in libunwindshow_backtrace()) 。

他们可能更好地支持框架解析/更好地与语言的虚拟机或展开信息集成。如果gdb(带有backtrace命令)或其他调试器无法从运行程序中获取堆栈跟踪,则可能根本无法获得堆栈跟踪。

如果他们可以获得调用跟踪,但是perf不能(即使在使用-fno-omit-frame-pointer重新编译C / C ++之后),也可以添加对这种架构+帧格式组合的支持进入perf_eventsperf

有几个博客提供了有关通用回溯问题和解决方案的一些信息:

perf_events / perf的矮人支持: