> [!NOTE]
> 本文档由 WeHub 基于上游 README 翻译整理,属于社区翻译,非官方中文文档。
> [English](./README.en.md) · [原始项目](https://github.com/janestreet/magic-trace) · [上游 README](https://github.com/janestreet/magic-trace/blob/HEAD/README.md)
> 原作者、版权与许可证归属以原始项目及本仓库 LICENSE 文件为准。
magic-trace
# 概述
magic-trace 收集并展示进程正在执行内容的高分辨率追踪(trace)记录。人们用它来:
- 查明为何生产环境中运行的应用在处理某些请求时很慢,同时又在应付海量无趣请求,
- 看清代码*实际*在做什么,而不是*以为*它在做什么,
- 获取应用崩溃前的执行历史,而不只是最后时刻的一张堆栈跟踪(stacktrace),
- ……以及更多用途!
magic-trace:
- 具有 [2%-10% 开销](https://github.com/janestreet/magic-trace/wiki/Overhead),
- 使用时不需修改应用,
- 以约 40ns 分辨率追踪*每一次函数调用*,并且
- 渲染可回溯约 10ms(可配置)的调用栈时间线。
用法类似 [`perf`](https://en.wikipedia.org/wiki/Perf_(Linux)): 指向一个进程即可开始运行。与 `perf` 的关键区别在于:magic-trace 不是随时间对调用栈采样,而是使用 [Intel Processor Trace](https://man7.org/linux/man-pages/man1/perf-intel-pt.1.html) 对截至某一选定时刻的*全部控制流*环形缓冲区做快照[^1]。然后你可以交互式地浏览时间线上发生的事。
你可以让 magic-trace 盯住某个函数,这样当应用调用它时,magic-trace 就会拍一张快照。或者附加到正在运行的进程上,用 Ctrl +C 分离,以查看程序任意时刻的追踪记录。
[^1]:`perf` 也能做到这一点,但多数人并不这么用它。事实上,若深入底层,你会发现 magic-trace 使用 `perf` 来驱动 Intel PT。
# 用户评价
> "Magic-trace 是我用过的最简单的命令行调试工具之一。"
- Francis Ricci, Jane Street
> "Magic-trace 不仅用于性能分析。它能直接揭示程序中发生了什么、何时发生以及为何发生。建议为满足一切自省(introspective)目标时都考虑使用它!"
- Andrew Hunter, Jane Street
> 我经常用 perf,我认为 perf 和 magic-trace 各自提供了对方没有的视角。我从 magic-trace 获益,完全是因为它能在任意缩放级别下按切片展示,因此我能看到某个 70ns 函数执行的所有函数调用,而在 perf 中这些内容完全不可见。
- Doug Patti, Jane Street
[更多用户评价……](https://github.com/janestreet/magic-trace/wiki/Unsolicited-reviews)
# 安装
1. 确保要追踪的系统[受支持](https://github.com/janestreet/magic-trace/wiki/Supported-platforms,-programming-languages,-and-runtimes). 最容易让人栽跟头的限制是:虚拟机大多不受支持、仅支持 Intel(Skylake[^3] 或更新)、仅支持 Linux。
2. 从[最新发布页](https://github.com/janestreet/magic-trace/releases/latest). 获取发布版二进制文件。
1. 若下载的是预编译二进制文件(非软件包),`chmod +x magic-trace`[^4]
1. 若下载的是软件包,运行 `sudo dpkg -i magic-trace*.deb`
然后运行 `magic-trace -help` 进行测试,应会显示一些帮助文本。
[^3]:严格来说,任何比 Broadwell 更新的平台都可以,但我们不会定期在该平台上测试,且时间分辨率更差(约 1us)。
[^4]:https://github.com/actions/upload-artifact/issues/38
# 快速入门
1. [这里](https://raw.githubusercontent.com/janestreet/magic-trace/master/demo/demo.c)'s 有一份可供试用的示例 C 程序。它是 `man 3 dlopen` 中示例的轻微修改版。下载后,用 `gcc demo.c -ldl -o demo` 编译,再以 `./demo` 保持运行。我们将用该程序学习 `dlopen` 的用法。
2. 运行 `magic-trace attach -pid $(pidof demo)`。看到已成功附加的提示后,等待几秒,然后对 `magic-trace` 按 Ctrl +C 。它会在当前工作目录输出名为 `trace.fxt.gz` 的文件。
3. 打开 [magic-trace.org](https://magic-trace.org/), 点击左上角的 _"Open trace file"_(打开追踪文件),并选择上一步生成的追踪文件。
4. 此时应已展开为一条追踪记录。放大直到能看到 `dlopen`/`dlsym`/`cos`/`printf`/`dlclose` 的单次循环。
- W 向鼠标光标所在位置放大(需要多次放大才能看到有用内容),
- S 缩小,
- A 向左移动,
- D 向右移动,以及
- 滚轮在调用栈上下移动视口。只有查看特别深的堆栈跟踪时才需要滚动,对本示例可能用处不大。
5. 在调用栈周围的空白处点击拖动以进行测量。在时间线顶部点击可插旗标。使用测量工具,测量运行 `cos` 的耗时。在我的屏幕上约为 ~5.7us。
恭喜,你刚刚神奇地完成了第一次程序追踪!
与传统 `perf` 工作流相比,magic-trace 擅长生成假设。例如,你可能会注意到运行 `cos` 耗时 6us 实在太长了!若再放大,你会看到其中实际上有五个粉色的 "\[untraced\]" 单元格。若以 root 重新运行 magic-trace 并传入 `-trace-include-kernel`,就能看到它们的堆栈跟踪。它们是页错误(page fault)处理程序!演示程序实际上调用了 `cos` 两次。若在 6us 的 `cos` 调用末尾附近再放大,你会看到第二次调用耗时*远*更少且不会发生页错误。
# 使用方法
magic-trace 将持续把控制流记录到环形缓冲区。在某种触发条件下,它对缓冲区拍快照并重建调用栈。
有两种拍快照的方式:
我们刚才做的就是其中一种:Ctrl +C 结束 magic-trace。若 magic-trace 在未拍过快照的情况下终止,会对程序末尾拍一张快照。
你也可以在应用调用某个函数时触发快照。做法是向 magic-trace 传入 `-trigger` 标志。
- `-trigger '?'` 会弹出模糊查找选择器,让你从可执行文件中的所有符号里选择,
- `-trigger SYMBOL` 选择你事先知道的某个具体、完整修饰(mangled)符号,以及
- `-trigger .` 选择默认符号 `magic_trace_stop_indicator`。
停止指示器(stop indicator)很强大。以下是一些你可能想放置它的场景:
- 若使用异步运行时,可在调度器周期耗时过长时放置。
- 在服务器中,当某个请求耗时出奇地长时。
- 垃圾回收器运行之后,查看它在做什么以及它打断了什么。
- 某个编译器 pass 完成之后。
你可以把停止指示器留在生产代码中。它不必执行任何特定操作,magic-trace 只需要函数名。它只是一个空函数,但不会被内联。调用它大约耗费 ~10us,但*仅在 magic-trace 实际用它拍快照时*才会产生该开销。
# 文档
更多文档可在 [magic-trace wiki](https://github.com/janestreet/magic-trace/wiki). 上查阅。
# 讨论
加入我们的 [Discord](https://discord.gg/vkzPYeZ292) 进行实时交流,或前往 [GitHub 讨论组](https://github.com/janestreet/magic-trace/discussions) 进行异步讨论。
# 贡献
如果你想贡献代码:
1. [阅读构建说明](https://github.com/janestreet/magic-trace/wiki/Build-instructions),
1. [配置你的编辑器](https://ocaml.org/learn/tutorials/up_and_running.html#Editor-support-for-OCaml),
1. [快速浏览代码库](https://github.com/janestreet/magic-trace/wiki/A-quick-tour-of-the-codebase),,然后
1. [查看 issue 跟踪器](https://github.com/janestreet/magic-trace/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 寻找合适的入门项目。
# 隐私政策
magic-trace 不会将你的代码或其衍生内容(包括 traces)发送到任何地方。
[magic-trace.org](https://magic-trace.org) 是 [Perfetto](https://github.com/janestreet/magic-trace/wiki/About-the-UI), 的轻度修改 fork,完全在浏览器中运行。据我们所知,它不会将 trace 发送到任何地方。如果你担心这一点日后可能改变,可以 [搭建本地 Perfetto UI 副本](https://github.com/janestreet/magic-trace/wiki/Setting-up-a-local-copy-of-the-UI) 并使用它。
# 致谢
[Tristan Hume](https://github.com/trishume) 是 magic-trace 的原始作者。他在 [Jane Street](https://www.janestreet.com/join-jane-street/), 工作期间编写了该项目,Jane Street 目前负责维护。
Intel PT(Intel Processor Trace)是 magic-trace 所依赖的基础技术。我们要感谢 Intel 的同仁多年来为使其可用所做的努力,尽管它在更广泛的软件社区中普及缓慢。
如果没有 `perf` 对 Intel PT 的广泛支持,magic-trace 就不可能存在。`perf` 承担了大部分解读 Intel PT 输出数据的工作,若没有他们的努力,magic-trace 很可能根本不会问世。感谢你们,`perf` 开发者。
[magic-trace.org](https://magic-trace.org) 是 [Perfetto](https://github.com/janestreet/magic-trace/wiki/About-the-UI), 的 fork,仅做了少量修改。我们要感谢 Google 负责该项目的同仁。这是一个高质量的代码库,很好地解决了一个难题。
magic-trace 背后的思想绝非独创。我们整理了一份 [先前工作](https://github.com/janestreet/magic-trace/wiki/Prior-art) 列表,记录了影响其设计的内容。