8个版本 (5个破坏性更改)
0.7.0 | 2024年6月28日 |
---|---|
0.6.0 |
|
0.5.0 | 2024年4月15日 |
0.4.1 | 2024年2月12日 |
0.1.1 | 2022年12月8日 |
#72 in 性能分析
每月 773次下载
在 3 crates 中使用
485KB
9K SLoC
wholesym
wholesym
是一个功能齐全的库,用于获取符号文件并将代码地址解析为符号和调试信息。它支持Windows、macOS和Linux。它非常快速,并针对最小化首次符号时间进行了优化。
使用方法如下
- 使用
SymbolManager
和SymbolManager::with_config
创建。 - 使用
SymbolMap
和SymbolManager::load_symbol_map_for_binary_at_path
加载。 - 使用
SymbolMap::lookup_relative_address
查找一个地址。 - 检查返回的
AddressInfo
,它提供符号名称,以及可能的文件和行信息,以及内联函数信息。
在幕后,wholesym
加载符号文件的方式类似于调试器。它支持符号服务器,从多个文件收集信息,以及将符号信息嵌入各种文件格式中的各种不同方式。
示例
use wholesym::{SymbolManager, SymbolManagerConfig, FramesLookupResult};
use std::path::Path;
# async fn run() -> Result<(), wholesym::Error> {
let symbol_manager = SymbolManager::with_config(SymbolManagerConfig::default());
let symbol_map = symbol_manager
.load_symbol_map_for_binary_at_path(Path::new("/usr/bin/ls"), None)
.await?;
println!("Looking up 0xd6f4 in /usr/bin/ls. Results:");
if let Some(address_info) = symbol_map.lookup_relative_address(0xd6f4) {
println!(
"Symbol: {:#x} {}",
address_info.symbol.address, address_info.symbol.name
);
let frames = match address_info.frames {
FramesLookupResult::Available(frames) => Some(frames),
FramesLookupResult::External(ext_ref) => {
symbol_manager
.lookup_external(&symbol_map.symbol_file_origin(), &ext_ref)
.await
}
FramesLookupResult::Unavailable => None,
};
if let Some(frames) = frames {
for (i, frame) in frames.into_iter().enumerate() {
let function = frame.function.unwrap();
let file = frame.file_path.unwrap().display_path();
let line = frame.line_number.unwrap();
println!(" #{i:02} {function} at {file}:{line}");
}
}
} else {
println!("No symbol for 0xd6f4 was found.");
}
# Ok(())
# }
此示例在我的机器上打印以下输出
Looking up 0xd6f4 in /usr/bin/ls. Results:
Symbol: 0xd5d4 gobble_file.constprop.0
#00 do_lstat at ./src/ls.c:1184
#01 gobble_file at ./src/ls.c:3403
该示例演示了对 debuglink
和 debugaltlink
的支持。它从本地调试文件 /usr/lib/debug/.build-id/63/260a3e6e46db57abf718f6a3562c6eedccf269.debug
和 /usr/lib/debug/.dwz/aarch64-linux-gnu/coreutils.debug
中获取符号信息,这些文件是由 coreutils-dbgsym
软件包安装的。如果这些文件不存在,它将回退到可用的任何信息。
特性
Windows
支持的符号文件来源
- 本地 PDB 文件,位于 .exe / .dll 中记录的绝对 PDB 路径
- Windows 符号服务器上的 PDB 文件 +
_NT_SYMBOL_PATH
环境变量 - Breakpad 符号文件,本地或服务器上的
- PE 中的 DWARF 调试信息
- 从导出函数和函数起始地址回退符号
目前不支持(接受补丁)
- 支持
/DEBUG:FASTLINK
PDB 文件(问题 #53)
macOS
支持的符号文件来源
- 本地 dSYM 包含符号表和 DWARF,位于二进制文件的附近
- 通过 Spotlight 找到的 dSYM 包
- 从链接二进制文件引用的对象文件中找到的 DWARF
- Breakpad 符号文件,本地或服务器上的
- 常规符号表中的符号
- 从导出函数和函数起始地址回退符号
目前不支持(接受补丁)
Linux
支持的符号文件来源
- 二进制文件中的 DWARF 和符号表
- 通过构建 ID 或调试链接找到的单独调试文件中的 DWARF 和符号表(分离调试文件)
- MiniDebugInfo 中的符号表(MiniDebugInfo)
- 使用
debugaltlink
(通过dwz
将调试信息部分移动)组合多个具有 DWARF 的文件 - debuginfod 服务器和
DEBUGINFOD_URLS
环境变量 - Breakpad 符号文件,本地或服务器上的
- 常规符号表中的符号
- 从导出函数和函数起始地址回退符号
- 使用 .dwo 文件拆分的 DWARF
- 使用 .dwp 文件拆分的 DWARF
性能
符号解析中最计算量大的部分是调试信息的解析。调试信息可能非常大,例如 Firefox 的 libxul 的 700MB 到 1500MB。 wholesym
使用 addr2line
和 pdb-addr2line
crate 分别解析 DWARF 和 PDB。它还有自己的代码来解析 Breakpad sym 格式。所有这些解析器都经过大量优化,以最大限度地减少获取第一个符号结果所需的时间,并缓存以使对相同函数的重复查找快速。这意味着
- 当符号文件首次加载时,不会发生昂贵的预处理。
- 解析尽可能懒惰:如果可能,我们只解析包含查找地址的函数所需的字节。
- 第一次解析尽可能浅且快,仅构建索引。
- 字符串(例如函数名称和文件路径)仅在需要时查找。
- 符号列表、行记录和内联代码在排序结构中缓存,并通过二分搜索查询。
依赖关系
~17–32MB
~554K SLoC