当前位置:   article > 正文

【翻译】 使用 SFrame 进行可靠的用户空间堆栈跟踪

【翻译】 使用 SFrame 进行可靠的用户空间堆栈跟踪
请考虑订阅 LWN

订阅是 LWN.net 的生命线。 如果您喜欢这些内容并希望看到更多,您的订阅将有助于确保 LWN 继续蓬勃发展。 请访问此页面加入我们,让 LWN 继续在网络上传播。

作者: Jonathan Corbet
2023 年 5 月 22 日
LSFMM+BPF
许多调试和优化任务都需要完整的堆栈跟踪,但要可靠地获得这些跟踪却具有惊人的挑战性。 在 2023 年 Linux 存储、文件系统、内存管理和 BPF 峰会上,Steve Rostedt 和 Indu Bhagat 介绍了一种名为 SFrame 的机制,该机制能够在内核中创建可靠的用户空间堆栈跟踪,而不会像其他一些解决方案那样产生内存和运行时开销。

[Steve Rostedt] Rostedt 首先指出,获取用户空间进程的完整堆栈跟踪有很多用途。 准确的剖析需要它,因此 perf 和 ftrace 都会使用堆栈跟踪。 BPF 程序也可以从调用堆栈的状态图中获益。

可靠获取堆栈帧的传统方法是使用帧指针构建相关程序。 帧指针只是一个 CPU 寄存器,专门用于包含当前堆栈帧的基地址。 该帧将包含上一帧指针的保存副本,指示上一帧的起始位置。 因此,内核(或任何其他程序)可以根据帧指针链来定位堆栈上的每一帧。 相反,如果没有帧指针,内核的perf子系统就必须在每次事件中复制大量堆栈,以便日后使用DWARF解卷器进行后处理。 这样做的成本很高。

但帧指针也不是免费的。 管理帧指针需要在每个函数的入口处运行一些设置代码。 为帧指针使用寄存器会使稀缺的 CPU 寄存器无法用于其他用途,从而减慢程序执行速度。 正如本文所述,使用帧指针构建用户空间会导致可测量的性能下降,这可能会导致帧指针的使用引起争议。

Rostedt 继续说,内核有一个名为ORC的堆栈解扰器,比 DWARF 简单得多。 它是在 4.14 版本中添加的,以支持实时修补--另一种需要可靠堆栈跟踪的应用。 内核的objtool工具会在构建时创建 ORC 数据,并将两个表添加到内核可执行文件的一个部分:orc_unwind表用于保存堆栈框架信息,orc_unwind_ip表用于将指令指针值映射到相应的解卷条目。

SFrame 基于 ORC;它提供了相同的机制,但针对的是用户空间而不是内核。 当使用 SFrame 数据构建可执行文件时,内核无需帧指针就能创建完整的堆栈跟踪。 当然,这样做总是要付出代价的;在这种情况下,开发人员需要牺牲一些磁盘空间(用于存放 ORC 表)来换取速度。 如果需要,这些数据会在内核的ptrace()路径中读取,因此在不需要时不会影响执行。 为了处理一些用户空间的复杂问题,还需要做一些额外的工作;例如,由于二进制文件是可重置的,因此必须有一种机制将正确的偏移量应用到 SFrame 数据上。

Rostedt 概述了 SFrame 支持如何在内核中运行。 perf事件的产生始于一个非屏蔽中断(NMI),它最终会进入perf代码。 如果需要堆栈跟踪,内核将尝试读取调用堆栈;如果遇到页面故障,则不会对该事件进行堆栈跟踪。 他希望将代码改为查找 SFrame 数据。 NMI 处理程序将设置一个标志,表明在返回用户空间之前还有工作要做;ptrace()路径将看到该标志,并在用户上下文中重建堆栈跟踪。 这样,即使在读取堆栈时发生页面故障,也能恢复堆栈。

这种方法还需要对用户空间的 perf 工具进行一些修改。 在 NMI 时生成的初始 perf 事件将不包括调用堆栈(稍后才会获得),因此它将设置一个位,表示 "堆栈跟踪即将发生"。 在堆栈跟踪最终出现在环中之前,可能会有多个事件发生。 Joel Fernandes 询问,内核是否可以在 NMI 时在环缓冲区中预留空间,然后再填入。 Rostedt 回答说,环中最终可能会出现多个具有相同堆栈跟踪的事件;为每个事件预留空间最终会浪费空间。

[Indu Bhagat] Rostedt 最后说,堆栈不太可能被换出,因此生成跟踪通常不会产生 I/O,也就不会将错误页面重新输入。 不过,由于 SFrame 表存储在磁盘上的可执行文件中,因此生成跟踪将需要引入一些其他数据。 SFrame 数据只有在实际使用时才会被映射,因此进程中的首次使用将导致映射发生时的短暂停滞。

巴加特(他为实现这一功能做了大量工作)说,内核中用汇编编写的部分代码可能会出现问题。 这些代码中的非标准堆栈用法很可能会混淆解卷器。 她说,通过内核的这些部分进行解卷是否重要还有待观察。

另一个潜在问题是,SFrame 数据在磁盘上的存储是非对齐的;这可能导致内核中的非对齐内存访问。 要避免这种情况,就需要进行一定量的数据复制、"奇异投递 "等操作,而强迫数据对齐则会使格式变得臃肿。 大家似乎一致认为,不对齐存储数据是最好的解决方案,没有必要进行更改。

其他悬而未决的问题包括需要处理dlopen(),它将另一个文件中的可执行文本映射到调用进程的内存范围中。 要解决这个问题,或许可以增加一个系统调用,告诉内核在哪里可以找到特定可执行文件映射的 SFrame 数据。 即时编译代码也是一个问题;当映射没有支持文件时,也就没有 SFrame 数据。

会议结束时,与会者似乎都认为 SFrame 是一个不错的工具,这项工作应该继续下去。



(登录以发表评论)

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/1007161
推荐阅读
相关标签
  

闽ICP备14008679号