Linux 的 perf 实用程序被 Brendan Gregg 用于为 c/c++、jvm 代码、nodejs 代码等生成火焰图。
Linux 内核本身是否理解堆栈跟踪?即使进程是用完全不同的语言编写的,我在哪里可以阅读更多关于工具如何自省进程堆栈跟踪的信息?
Linux 的 perf 实用程序被 Brendan Gregg 用于为 c/c++、jvm 代码、nodejs 代码等生成火焰图。
Linux 内核本身是否理解堆栈跟踪?即使进程是用完全不同的语言编写的,我在哪里可以阅读更多关于工具如何自省进程堆栈跟踪的信息?
perf
Gregg
中有关于堆栈跟踪的简短介绍: 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 和 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
”)返回的数据,使用 syscall 访问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.
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
功能,或者在 libunwind 中支持只读展开(在 libunwind 中有本地回溯示例,show_backtrace()
)。
他们可能更好地支持帧解析/与语言的虚拟机或展开信息更好地集成。如果 gdb(使用backtrace
命令)或其他调试器无法从正在运行的程序中获取堆栈跟踪,则可能根本无法获取堆栈跟踪。
如果他们可以获得调用跟踪,但perf
不能(即使在使用-fno-omit-frame-pointer
for C/C++ 重新编译后),则可以在perf_events
and perf
.
有几个博客包含一些关于通用回溯问题和解决方案的信息:
__builtin_return_address(N)
vs glibc's backtrace()
vs libunwind's local backtrace矮人对perf_events
/的支持perf
: