使用 DTrace 在 Rust 上获取堆栈跟踪/分析数据

Posted

技术标签:

【中文标题】使用 DTrace 在 Rust 上获取堆栈跟踪/分析数据【英文标题】:Using DTrace to get stack traces / profiling data on Rust 【发布时间】:2017-04-18 09:57:30 【问题描述】:

我正在尝试获取我的 Rust 代码的漂亮火焰图。不幸的是,Xcode 8.3 不再支持导出分析数据,所以我一直在尝试使用 DTrace 来获取分析数据。

我在我的Cargo.toml 中为发布二进制文件启用了调试信息:

[profile.release]
debug = true

然后我运行发布二进制文件 (mybinaryname),并使用 DTrace 对堆栈跟踪进行示例:

sudo dtrace -n 'profile-997 /execname == "mybinaryname"/  @[ustack(100)] = count(); ' -o out.user_stacks

最终结果是这样的:

          0x10e960500
          0x10e964632
          0x10e9659e0
          0x10e937edd
          0x10e92aae2
          0x10e92d0d7
          0x10e982c8b
          0x10e981fc1
          0x7fff93c70235
          0x1
            1

作为比较,获取iTerm2 的痕迹让我得到了很好的痕迹,如下所示:

          CoreFoundation`-[__NSArrayM removeAllObjects]
          AppKit`_NSGestureRecognizerUpdate+0x769
          CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__+0x17
          CoreFoundation`__CFRunLoopDoObservers+0x187
          CoreFoundation`__CFRunLoopRun+0x4be
          CoreFoundation`CFRunLoopRunSpecific+0x1a4
          HIToolbox`RunCurrentEventLoopInMode+0xf0
          HIToolbox`ReceiveNextEventCommon+0x1b0
          HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter+0x47
          AppKit`_DPSNextEvent+0x460
          AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+0xaec
          AppKit`-[NSApplication run]+0x39e
          AppKit`NSApplicationMain+0x4d5
          iTerm2`main+0x6e
          libdyld.dylib`start+0x1
          iTerm2`0x1
            1

是否可以在 Rust 代码中获取带有调试信息的堆栈跟踪? (Xcode 的 Instruments 肯定可以看到函数名称,所以它们就在那里!)如果可能的话,我是否需要采取一些额外的步骤,或者我只是做错了什么?

【问题讨论】:

会不会和issue #24346有关,参考this question? @E_net4 我不这么认为。这个问题指的是 Rust 本身内置的回溯功能。列出的解决方法之一是使用调试器,它更好地知道如何使用调试符号来获取回溯。像 OP 节目一样,Instruments 也知道这些符号。 有没有办法告诉 dtrace 在哪里寻找符号?我想知道 Rust 放置 .dSYM 文件的位置是否有什么不同导致 dtrace 无法找到它们。 我会检查的! (其实我不知道调试信息是怎么存储的,谢谢指点。) 现在更奇怪了。对于 ONE 运行,它碰巧找到了调试符号。我之后追踪的类似运行没有找到符号 - 不确定性!顺便提一句。我怀疑这与我之前在使用 backtrace crate 时遇到的问题有关:github.com/brson/error-chain/issues/129#issuecomment-281946612 在 Apple 平台上,使用了一个名为 CoreSymbolication 的框架。 (逆向工程尝试:github.com/mountainstorm/CoreSymbolication) 【参考方案1】:

我找到了一种解决方法,并了解了为什么它可能不起作用,但原因并不是 100% 清楚。

rustc 产生的调试符号可以在target/release/deps/mybinaryname-hashcode.dSYM 中找到。在同一目录下有一个二进制文件target/release/deps/mybinaryname-hashcode,符号对应。

MacOS 上的调试符号查找库非常神奇 - 就像 mentioned in the LLDB docs 一样,可以使用各种方法查找符号,包括 Spotlight 搜索。我什至不确定 Xcode 的 Instruments 和捆绑的 DTrace 使用的是哪些框架。 (有人提到了称为 DebugSymbols.framework 和 CoreSymbolication.framework 的框架。)由于这种魔力,我放弃了尝试理解为什么它不起作用。

解决方法是传递dtrace -p 选项以及被检查进程的PID:

sudo dtrace -p $PID -n 'profile-997 /pid == '$PID'/  @[ustack(100)] = count(); ' -o $TMPFILE &>/dev/null

这是-pman

获取指定的进程 ID pid,缓存其符号表,并在完成后退出。如果命令行上存在多个 -p 选项,则 dtrace 会在所有命令退出时退出,并在每个进程终止时报告其退出状态。第一个进程 ID 可用于命令行中指定的任何 D 程序或通过 $target 宏变量使用 -s 选项。

不清楚为什么默认显示各种其他二进制文件的调试信息,或者为什么 Rust 二进制文件需要 -p 选项,但它作为一种解决方法发挥了作用。

【讨论】:

我后来发现调试符号的目录在 Spotlight 的阻止列表中。删除列表条目并让 Spotlight 重新索引解决了问题。

以上是关于使用 DTrace 在 Rust 上获取堆栈跟踪/分析数据的主要内容,如果未能解决你的问题,请参考以下文章

跟踪一个短暂的应用程序

在 Oracle Linux 上使用 DTrace

与命令行相比,来自 C 的 Dtrace 不会产生相同的分析结果

如何使用 dtrace 跟踪函数?

如何使用 dtrace 查看调用堆栈

当我在 MacOS 中跟踪“malloc:return”时,DTrace 报告不正确的值