Linux的性能分析工具perf被广泛用于生成c/c++、JVM代码、Node.js代码等的火焰图。
Linux内核本质上能否理解堆栈跟踪信息?我在哪里可以阅读有关工具如何审查进程的堆栈跟踪信息的更多信息,即使这些进程是以完全不同的语言编写的?
Linux的性能分析工具perf被广泛用于生成c/c++、JVM代码、Node.js代码等的火焰图。
Linux内核本质上能否理解堆栈跟踪信息?我在哪里可以阅读有关工具如何审查进程的堆栈跟踪信息的更多信息,即使这些进程是以完全不同的语言编写的?
这里有一篇关于perf
中堆栈跟踪的简短介绍,作者是Gregg:
http://www.brendangregg.com/perf.html
4.4 堆栈跟踪
始终使用帧指针进行编译。省略帧指针是一种邪恶的编译器优化,会破坏调试器,并且往往是默认设置。如果没有帧指针,则可能会在perf_events中看到不完整的堆栈... 有两种解决方法:使用dwarf数据展开堆栈或返回帧指针。
Dwarf
自3.9内核以来,perf_events已支持用户级堆栈中缺少帧指针的解决方法:libunwind,它使用dwarf。可以使用"-g dwarf"启用此功能。 ... 编译器优化(
-O2
),在这种情况下省略了帧指针。... 使用-fno-omit-frame-pointer
重新编译:
非C语言可能具有不同的帧格式,或者也可能省略帧指针:
4.3. JIT 符号 (Java, Node.js)
拥有虚拟机 (VMs) 的程序,如 Java 的 JVM 和 node 的 v8,在执行函数和管理堆栈时都有自己的虚拟处理器。如果使用 perf_events 进行分析,你会看到 VM 引擎的符号...perf_events 有 JIT 支持来解决这个问题,需要 VM 维护一个
/tmp/perf-PID.map
文件进行符号转换。请注意,由于 x86 上的热点省略了帧指针(就像 gcc 一样),因此 Java 可能一开始不会显示完整的堆栈。在较新版本中(JDK 8u60+),您可以使用
-XX:+PreserveFramePointer
选项来修复此行为...
Gregg 关于 Java 和堆栈跟踪的博客文章: http://techblog.netflix.com/2015/07/java-in-flames.html ("Fixing Frame Pointers" - 在某些 JDK8 版本中已经修复,并在启动程序时添加了 JDK9 选项)
现在,你的问题:
linux 的 perf 工具是如何理解堆栈跟踪的?
perf
实用工具 基本上(在早期版本中)只是解析从Linux内核子系统 "perf_events
"(或有时是 "events
")返回的数据,通过系统调用 perf_event_open
进行访问。对于调用堆栈跟踪,可以使用选项 PERF_SAMPLE_CALLCHAIN
/ PERF_SAMPLE_STACK_USER
:
sample_type PERF_SAMPLE_CALLCHAIN 记录函数调用链(堆栈回溯)。
PERF_SAMPLE_STACK_USER (since Linux 3.7)
Records the user level stack, allowing stack unwinding.
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 }
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
函数或libunwind中只读展开的支持(其中包含一个本地回溯示例在libunwind中,show_backtrace()
)。
它们可能会更好地支持帧解析/与语言的虚拟机或展开信息的集成。如果gdb(使用backtrace
命令)或其他调试器无法从运行程序中获取堆栈跟踪,则可能没有办法获取堆栈跟踪。
perf
不能(即使使用-fno-omit-frame-pointer
重新编译C / C ++后),可能可以将架构和帧格式的此类组合添加到perf_events
和perf
中进行支持。__builtin_return_address(N)
与glibc的backtrace()
以及libunwind的本地回溯比较perf_events
/perf
的Dwarf支持:
perf record
命令中增加--call-graph dwarf,81920
参数可以获取更详细的调用堆栈。 - osgx