linux 的 perf 实用程序如何理解堆栈跟踪? [英] How does linux's perf utility understand stack traces?

查看:15
本文介绍了linux 的 perf 实用程序如何理解堆栈跟踪?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

解决方案

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 文件用于符号翻译.

请注意,由于 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 utility 基本上(在早期版本中)只是解析从 linux 内核子系统perf_events"(或有时events"),通过系统调用访问 perf_event_open.对于调用堆栈跟踪,有选项 PERF_SAMPLE_CALLCHAIN/PERF_SAMPLE_STACK_USER:

样本类型PERF_SAMPLE_CALLCHAIN记录调用链(堆栈回溯).

 PERF_SAMPLE_STACK_USER (Linux 3.7 起)记录用户级堆栈,允许堆栈展开.

<块引用>

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 结构 pt_regs *regs)29 {30 }3132 __weak void perf_callchain_user(struct perf_callchain_entry *entry,33 结构 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,第 2279 行arch/arc/kernel/perf_event.c,第 72 行arch/sparc/kernel/perf_event.c,第 1829 行arch/arm/kernel/perf_callchain.c,第 62 行arch/xtensa/kernel/perf_event.c,第 339 行arch/tile/kernel/perf_event.c,第 995 行arch/arm64/kernel/perf_callchain.c,第 109 行arch/powerpc/perf/callchain.c,第 490 行arch/metag/kernel/perf_callchain.c,第 59 行

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

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

<块引用>

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

您可以尝试 gdb 和/或该语言的调试器或 libc的backtrace函数或libunwind中支持只读展开(有libunwind 中的本地回溯示例show_backtrace()).

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

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

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

矮人支持perf_events/perf:

Linux's perf utility is famously used by Brendan Gregg to generate flamegraphs for c/c++, jvm code, nodejs code, etc.

Does the Linux kernel natively understand stack traces? 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?

解决方案

There is short introduction about stack traces in perf by Gregg: 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:

Non C-style languages may have different frame format, or may omit frame pointers too:

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, ...

The Gregg's blog post about Java and stack traces: http://techblog.netflix.com/2015/07/java-in-flames.html ("Fixing Frame Pointers" - fixed in some JDK8 versions and in JDK9 by adding option on program start)

Now, your questions:

How does linux's perf utility understand stack traces?

perf utility basically (in early versions) just parses data returned from linux kernel's subsystem "perf_events" (or sometimes "events"), accessed with syscall perf_event_open. For call stack trace there are options PERF_SAMPLE_CALLCHAIN / PERF_SAMPLE_STACK_USER:

sample_type PERF_SAMPLE_CALLCHAIN Records the callchain (stack backtrace).

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

Does the Linux kernel natively understand stack traces?

It may understand (if implemented) and may not, depending on your cpu architecture. The function of sampling (getting/reading call stack from live process) callchain is defined in architecture-independent part of kernel as __weak with empty body:

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 }

In 4.4 kernel user-space callchain sampler is redefined in architecture-dependent part of kernel for 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

Reading of call chain from user stack may be not trivial for some architectures and/or for some modes.

What CPU architecture you use? What languages and VM are used?

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?

You may try gdb and/or debuggers for the language or backtrace function of libc or support of read-only unwinding in libunwind (there is local backtrace example in libunwind, show_backtrace()).

They may have better support of frame parsing / better integration with virtual machine of the language or with unwind info. If gdb (with backtrace command) or other debuggers can't get stack traces from running program, there may be no way of getting stack trace at all.

If they can get call trace, but perf can't (even after recompiling with -fno-omit-frame-pointer for C/C++), it may be possible to add support of such combination of architecture + frame format into perf_events and perf.

There are several blogs with some info about generic backtracing problems and solutions:

Dwarf support for perf_events/perf:

这篇关于linux 的 perf 实用程序如何理解堆栈跟踪?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆