Linux的perf实用程序被Brendan Gregg著名地用于为c/c++、jvm代码、nodejs代码等生成火焰图。
Linux内核本身是否理解堆栈跟踪?我在哪里可以阅读更多关于工具如何能够内省进程的堆栈跟踪的信息,即使进程是用完全不同的语言编写的?
Gregg对perf
中的堆栈跟踪进行了简短介绍:http://www.brendangregg.com/perf.html
4.4堆栈跟踪
始终使用帧指针进行编译。省略帧指针是一种邪恶的编译器优化,会破坏调试器,不幸的是,这通常是默认的。如果没有它们,您可能会从perf_events中看到不完整的堆栈。。。有两种方法可以解决这个问题:要么使用侏儒数据展开堆栈,要么返回帧指针。
矮
从3.9内核开始,perf_events就支持一种解决用户级堆栈中缺少帧指针的方法:libunvell,它使用dwarf。这可以使用"-g侏儒"来启用。…编译器优化(
-O2
),在本例中省略了帧指针。。。正在重新编译。。带-fno-omit-frame-pointer
:
非C风格语言可能有不同的帧格式,或者也可能省略帧指针:
4.3.JIT符号(Java,Node.js)
具有虚拟机(VM)的程序,如Java的JVM和node的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_typePERF_SAMPLE_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内核中,用户空间调用链采样器在内核的体系结构相关部分被重新定义,用于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架构是什么?使用了哪些语言和虚拟机?
在哪里可以阅读更多关于工具如何能够内省进程的堆栈跟踪的信息,即使进程是用完全不同的语言编写的?
您可以尝试使用gdb
和/或调试器来实现libc的语言或backtrace
函数,或者在libunvell中支持只读展开(在libunvel中有本地回溯示例,show_backtrace()
)。
它们可以更好地支持帧解析/更好地与该语言的虚拟机或展开信息集成。若gdb(使用backtrace
命令)或其他调试器无法从运行的程序中获取堆栈跟踪,则可能根本无法获取堆栈跟踪。
如果它们可以获得调用跟踪,但perf
不能(即使在用-fno-omit-frame-pointer
为C/C++重新编译之后),则可以在perf_events
和perf
中添加对架构+帧格式的这种组合的支持。
有几个博客提供了一些关于一般回溯问题和解决方案的信息:
- http://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/-带有libunvent的本地回溯
- http://codingrelic.geekhold.com/2009/05/pre-mortem-backtracing.htmlgcc的
__builtin_return_address(N)
与glibc的backtrace()
与libunfind的本地回溯 - http://lucumr.pocoo.org/2014/10/30/dont-panic/铁锈中的回溯和展开
- https://github.com/gperftools/gperftools/wiki/gperftools堆栈跟踪捕获方法及其问题gperftools软件中基于定时器的探查器库中的回溯问题
对perf_events
/perf
:的Dwarf支持
- https://lwn.net/Articles/499116/[RFCv4 00/16]perf:添加回溯后侏儒展开,2012年5月
- https://lwn.net/Articles/507753/〔PATCHv7 00/17〕性能:添加回溯后侏儒展开,2012年7月
- https://wiki.linaro.org/LEG/Engineering/TOOLS/perf-callstack-unwinding-在ARM 7/8上进行矮展开,以实现性能
- https://wiki.linaro.org/KenWerner/Sandbox/libunwind#libunwind_ARM_unwind_methods-非矮化方法