#symbols #symbol-name #file-path #debug-info #addr2line #file-line

wholesym

一个完整的解决方案,用于获取符号文件并将代码地址解析为符号和调试信息

8个版本 (5个破坏性更改)

0.7.0 2024年6月28日
0.6.0 2024年6月20日
0.5.0 2024年4月15日
0.4.1 2024年2月12日
0.1.1 2022年12月8日

#72 in 性能分析

Download history 520/week @ 2024-04-15 295/week @ 2024-04-22 215/week @ 2024-04-29 251/week @ 2024-05-06 198/week @ 2024-05-13 283/week @ 2024-05-20 264/week @ 2024-05-27 407/week @ 2024-06-03 258/week @ 2024-06-10 409/week @ 2024-06-17 383/week @ 2024-06-24 229/week @ 2024-07-01 190/week @ 2024-07-08 212/week @ 2024-07-15 164/week @ 2024-07-22 196/week @ 2024-07-29

每月 773次下载
3 crates 中使用

MIT/Apache

485KB
9K SLoC

wholesym

wholesym 是一个功能齐全的库,用于获取符号文件并将代码地址解析为符号和调试信息。它支持Windows、macOS和Linux。它非常快速,并针对最小化首次符号时间进行了优化。

使用方法如下

  1. 使用 SymbolManagerSymbolManager::with_config 创建。
  2. 使用 SymbolMapSymbolManager::load_symbol_map_for_binary_at_path 加载。
  3. 使用 SymbolMap::lookup_relative_address 查找一个地址。
  4. 检查返回的 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

该示例演示了对 debuglinkdebugaltlink 的支持。它从本地调试文件 /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 使用 addr2linepdb-addr2line crate 分别解析 DWARF 和 PDB。它还有自己的代码来解析 Breakpad sym 格式。所有这些解析器都经过大量优化,以最大限度地减少获取第一个符号结果所需的时间,并缓存以使对相同函数的重复查找快速。这意味着

  • 当符号文件首次加载时,不会发生昂贵的预处理。
  • 解析尽可能懒惰:如果可能,我们只解析包含查找地址的函数所需的字节。
  • 第一次解析尽可能浅且快,仅构建索引。
  • 字符串(例如函数名称和文件路径)仅在需要时查找。
  • 符号列表、行记录和内联代码在排序结构中缓存,并通过二分搜索查询。

依赖关系

~17–32MB
~554K SLoC