Linux kernel linux';s perf实用程序是否了解堆栈跟踪?

Linux kernel linux';s perf实用程序是否了解堆栈跟踪?,linux-kernel,perf,flamegraph,Linux Kernel,Perf,Flamegraph,众所周知,Brendan Gregg使用Linux的perf实用程序为c/c++、jvm代码、nodejs代码等生成FlameGraph Linux内核是否本机理解堆栈跟踪?在哪里可以阅读更多关于工具如何能够内省进程的堆栈跟踪的信息,即使进程是用完全不同的语言编写的?Gregg在perf中对堆栈跟踪做了简短介绍: 4.4堆栈跟踪 始终使用帧指针编译。忽略帧指针是一种有害的编译器优化,会破坏调试器,遗憾的是,这通常是默认的。如果没有它们,您可能会看到性能事件的不完整堆栈。。。有两种方法可以解决这

众所周知,Brendan Gregg使用Linux的perf实用程序为c/c++、jvm代码、nodejs代码等生成FlameGraph


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

Gregg在
perf
中对堆栈跟踪做了简短介绍:

4.4堆栈跟踪

始终使用帧指针编译。忽略帧指针是一种有害的编译器优化,会破坏调试器,遗憾的是,这通常是默认的。如果没有它们,您可能会看到性能事件的不完整堆栈。。。有两种方法可以解决这个问题:要么使用dwarf数据展开堆栈,要么返回帧指针

矮子

从3.9内核开始,perf_事件就支持解决用户级堆栈中缺少帧指针的问题:libunwind,它使用dwarf。这可以使用“-g dwarf”启用。 ... 编译器优化(
-O2
),在本例中省略了帧指针。。。重新编译。。使用
-fno省略帧指针

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

4.3。JIT符号(Java、Node.js)

具有虚拟机(VM)的程序,如Java的JVM和node的v8,执行自己的虚拟处理器,它有自己的执行函数和管理堆栈的方式。如果您使用perf_事件分析这些事件,您将看到VM引擎的符号。。perf_events具有JIT支持来解决此问题,这需要VM维护一个
/tmp/perf PID.map
文件以进行符号转换

请注意,由于x86上的热点省略了帧指针(就像gcc一样),Java可能不会首先显示完整的堆栈。在较新版本(JDK 8u60+)上,您可以使用
-XX:+PreserveFramePointer
选项修复此行为

Gregg关于Java和堆栈跟踪的博文: (“修复帧指针”-通过在程序启动时添加选项在某些JDK8版本和JDK9中修复)

现在,你的问题是:

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

perf
实用程序基本上(在早期版本中)只解析从linux内核的子系统“
perf_events
”(有时是“
events
”)返回的数据,通过syscall访问。对于调用堆栈跟踪,有选项
PERF\u SAMPLE\u CALLCHAIN
/
PERF\u SAMPLE\u stack\u USER

样本类型 性能示例调用链 记录调用链(堆栈回溯)

Linux内核是否本机理解堆栈跟踪

它可能理解(如果实现)也可能不理解,这取决于您的cpu体系结构。内核的体系结构独立部分将采样(从实时进程获取/读取调用堆栈)调用链的功能定义为
\uu-weak
,主体为空:

在4.4内核中,用户空间调用链采样器在x86/x86_64、ARC、SPARC、ARM/ARM64、Xtensa、Tilera TILE、PowerPC、Imagination Meta内核的体系结构相关部分重新定义:

对于某些体系结构和/或某些模式,从用户堆栈读取调用链可能并不容易

您使用什么样的CPU体系结构?使用什么语言和虚拟机


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

您可以尝试使用该语言或libc的
gdb
和/或调试器,或在libunwind中支持只读展开(有,
show\u backtrace()

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

如果它们可以获得调用跟踪,但是
perf
不能(即使在使用
-fno省略C/C++的帧指针之后),则可以将这种架构+帧格式组合的支持添加到
perf_事件
perf

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

  • -带libunwind的本地回溯
  • gcc的
    \u内置\u返回\u地址(N)
    与glibc的
    backtrace()
  • 锈蚀中的回描和退卷
  • “-stacktrace捕获方法及其问题与gperftools软件基于计时器的探查器库中的回溯问题相同
对性能事件的Dwarf支持
/
perf

  • [RFCv4 00/16]性能:添加回溯后侏儒展开,2012年5月
  • [PATCHv7 00/17]性能:添加回溯后展开,2012年7月
  • -臂7/8上的矮型退绕,用于性能测试
  • -非矮化方法

好吧,这不是进程源代码所使用的语言的问题,而是运行的二进制文件的问题。最终,语言代码被转换为二进制(可执行文件),并在处理器上运行。linux的可执行文件可能是格式化的。堆栈跟踪是为这个二进制代码生成的。如果二进制文件有描述其“过程”的符号表,那么在跟踪中会有一些普通名称。(Brendan Gregg描述了Java缺少符号的问题,火焰图上相应的空白部分。)@xealits你有Brendan关于Java缺少符号的注释的链接吗?这可能会为我填补缺失的部分;有时linux内核很难找到完整的调用堆栈——编译器优化可能会忽略一些帧指针。一些Java虚拟机具有堆栈帧格式的魔力:“4.4
          PERF_SAMPLE_STACK_USER (since Linux 3.7)
                 Records the user level stack, allowing stack unwinding.
 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 }
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