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

How does linux's perf utility understand stack traces?

Linux 的 perf 实用程序被 Brendan Gregg 著名地用于为 c/c++、jvm 代码、nodejs 代码等生成火焰图。

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

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

4.4 Stack Traces

Always compile with frame pointers. Omitting frame pointers is an evil compiler optimization that breaks debuggers, and sadly, is often the default. Without them, you may see incomplete stacks from perf_events ... There are two ways to fix this: either using dwarf data to unwind the stack, or returning the frame pointers.

Dwarf

Since about the 3.9 kernel, perf_events has supported a workaround for missing frame pointers in user-level stacks: libunwind, which uses dwarf. This can be enabled using "-g dwarf". ... compiler optimizations (-O2), which in this case has omitted the frame pointer. ... recompiling .. with -fno-omit-frame-pointer:

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

4.3. JIT Symbols (Java, Node.js)

Programs that have virtual machines (VMs), like Java's JVM and node's v8, execute their own virtual processor, which has its own way of executing functions and managing stacks. If you profile these using perf_events, you'll see symbols for the VM engine .. perf_events has JIT support to solve this, which requires the VM to maintain a /tmp/perf-PID.map file for symbol translation.

Note that Java may not show full stacks to begin with, due to hotspot on x86 omitting the frame pointer (just like gcc). On newer versions (JDK 8u60+), you can use the -XX:+PreserveFramePointer option to fix this behavior, ...

Gregg 的博客 post 关于 Java 和堆栈跟踪: http://techblog.netflix.com/2015/07/java-in-flames.html("Fixing Frame Pointers" - 通过在程序启动时添加选项在某些 JDK8 版本和 JDK9 中修复)

现在,您的问题:

How does linux's perf utility understand stack traces?

perf utility 基本上(在早期版本中)只是解析从 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.

Does the Linux kernel natively understand stack traces?

它可能理解(如果实现)也可能不理解,这取决于您的 cpu 体系结构。采样函数(getting/reading来自实时进程的调用堆栈)调用链在内核的体系结构无关部分定义为__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 内核用户中-space 调用链采样器在内核的架构相关部分重新定义为 x86/x86_64、ARC、SPARC、ARM/ARM64、Xtensa、Tilera TILE、PowerPC、想象力元:

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

对于某些架构,从用户堆栈读取调用链可能并不简单and/or对于某些模式。

您使用什么 CPU 架构?使用什么语言和虚拟机?

Where can I read more about how a tool is able to introspect into stack traces of processes, even if processes are written in completely different languages?

您可以尝试 gdb and/or 语言调试器或 backtrace function of libc or support of read-only unwinding in libunwind (there is local backtrace example in libunwindshow_backtrace())。

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

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

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

perf_events/perf 的矮人支持: