使用perf分析Rust二进制程序时如何规避意外栈帧?
用perf分析Rust程序时如何消除意外栈帧?
我使用perf record对Rust二进制程序进行性能分析,在perf script的输出中发现了意外栈帧,导致火焰图难以解读。
分析命令:
$ perf record -F 997 --call-graph dwarf,65528 -g -e cpu-clock /path/to/bin $ perf script
程序中fn main调用fn tip_mine,以下是符合预期的栈帧记录:
... 56275bcc4bee stacks_inspect::tip_mine+0xd23e (inlined) 56275bcc4bee stacks_inspect::main+0xd23e (/home/igor/stacks-blockchain/target/release/stacks-inspect) ffffffffffffffff [unknown] ([unknown])
但同一次运行中,perf script还输出了包含额外栈帧的记录,这些栈帧出现在fn main和fn tip_mine之间:
... 56275bcbe20d stacks_inspect::tip_mine+0x685d (inlined) 56275bcbe20d stacks_inspect::main+0x685d (/home/igor/stacks-blockchain/target/release/stacks-inspect) 56275bc9bc42 core::ops::function::FnOnce::call_once+0x2 (inlined) 56275bc9bc42 std::sys_common::backtrace::__rust_begin_short_backtrace+0x2 (/home/igor/stacks-blockchain/target/release/stacks-inspect) 56275bca5e68 std::rt::lang_start::{{closure}}+0x8 (inlined) 56275c35dc8d core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once+0x42d (inlined) 56275c35dc8d std::panicking::try::do_call+0x42d (inlined) 56275c35dc8d std::panicking::try+0x42d (inlined) 56275c35dc8d std::panic::catch_unwind+0x42d (inlined) 56275c35dc8d std::rt::lang_start_internal::{{closure}}+0x42d (inlined) 56275c35dc8d std::panicking::try::do_call+0x42d (inlined) 56275c35dc8d std::panicking::try+0x42d (inlined) 56275c35dc8d std::panic::catch_unwind+0x42d (inlined) 56275c35dc8d std::rt::lang_start_internal+0x42d (/home/igor/stacks-blockchain/target/release/stacks-inspect) 56275bcc64e7 main+0x27 (/home/igor/stacks-blockchain/target/release/stacks-inspect) 7fa0aa8b4d8f [unknown] (/usr/lib/x86_64-linux-gnu/libc.so.6) 7fa0aa8b4e3f __libc_start_main+0x7f (/usr/lib/x86_64-linux-gnu/libc.so.6) 56275bc5a1d4 _start+0x24 (/home/igor/stacks-blockchain/target/release/stacks-inspect)
解决方法
1. 编译时关闭panic unwind机制
Rust默认用unwind处理panic,会生成大量标准库相关的栈帧。改用abort策略可以消除这些额外栈帧:
在项目的Cargo.toml中添加:
[profile.release] panic = "abort"
修改后重新编译程序,再执行perf分析。
2. 后处理perf输出过滤栈帧
直接在perf script输出中过滤掉Rust标准库的辅助函数,只保留业务代码栈帧:
- 仅保留自己项目的代码和main函数:
perf script | grep -E 'stacks_inspect::|main'
- 过滤掉
core::、std::开头的标准库栈帧:
perf script | awk '!/core::|std::/'
3. 改用帧指针采集调用图
默认情况下Rust编译会省略帧指针,开启后可以用fp模式采集更简洁的栈:
首先在Cargo.toml中开启帧指针:
[profile.release] rustflags = ["-C", "force-frame-pointers=yes"]
重新编译后,用以下命令执行perf:
perf record -F 997 --call-graph fp -g -e cpu-clock /path/to/bin
4. 火焰图工具层面过滤
如果使用flamegraph.pl生成火焰图,可以通过--exclude参数直接过滤不需要的函数前缀:
perf script | flamegraph.pl --exclude 'core::|std::' > flame.svg
内容的提问来源于stack exchange,提问作者user19777296
相关产品推荐
相关产品推荐

