30个版本 (15个破坏性版本)

0.16.1 2022年7月14日
0.15.0 2022年5月23日
0.11.2 2022年2月10日
0.11.1 2021年12月12日
0.1.0 2020年5月28日

#1225 in 编码

Download history 1/week @ 2024-03-11 25/week @ 2024-04-01

89 每月下载量
2 个crate中使用 (通过 profiler-symbol-server)

MIT/Apache

185KB
3.5K SLoC

profiler-get-symbols

这是profiler-get-symbols 工作区的 "库" 部分。这个crate暴露了三个函数: get_compact_symbol_tableget_symbolication_resultquery_api。这些函数允许各种消费者获取符号信息。

请查看完整仓库以获取详细信息。


lib.rs:

此crate允许从二进制文件和编译工件中获取符号信息。它将原始代码地址映射到符号字符串,以及可选的文件名+行号信息。API是为Firefox分析器设计的。

此crate的主要入口点是异步的 query_api 函数,它接受一个包含查询输入的JSON字符串。JSON API与Mozilla符号化服务器("Tecken")的API相匹配。还有一个可选的无JSON API,但它不太方便。

设计约束

此crate在以下设计约束下运行

  • 必须可在JavaScript / WebAssembly中使用:Firefox分析器在Firefox内部的权限JavaScript代码中运行此代码,在WebAssembly环境中。这种设置允许我们按需下载profiler-get-symbols wasm包,而不是将其与Firefox一起分发,这将增加Firefox下载大小,但对于绝大多数Firefox用户来说,这是不必要的。
  • 性能:我们希望尽可能快地从本地编译的Firefox实例的新构建中获取符号数据,而不需要昂贵的预处理步骤。从“编译完成”到“返回符号数据”的时间应尽可能短。这意味着符号数据需要直接从编译工件中获取,而不是从,比如说,dSYM包或Breakpad .sym文件中获取。
  • 必须扩展到大型输入:这适用于API请求的大小以及需要解析的对象文件的大小:Firefox分析器将在单个符号化请求中提供从数万到数十万个不同的代码地址。Firefox构建工件,如libxul.so,可以大达数GB,包含大约300000个函数符号。我们希望几秒钟或更少的时间内处理此类请求。
  • “尽力而为”的原则:如果只有有限的符号信息可用,例如来自系统库,我们希望返回我们所拥有的任何有限信息。

WebAssembly要求意味着这个crate不能包含任何直接文件访问。相反,所有文件访问都通过一个FileAndPathHelper trait进行中介,该trait必须由调用者实现。此外,API请求不包含任何绝对文件路径,因此绝对文件路径的解析也需要由调用者完成。

支持的格式和数据

这个crate支持从PE二进制文件(Windows)、PDB文件(Windows)、mach-o二进制文件(包括胖二进制文件)(macOS & iOS)和ELF二进制文件(Linux、Android等)中获取符号数据。对于mach-o文件,它还支持通过遵循OSO stab条目在外部对象中查找调试信息。它支持收集基本符号信息(函数名字符串)以及基于调试数据的基于调试数据的信息,即每个帧都有一个函数名、一个文件名和一个行号的内联调用栈。对于调试数据,我们支持DWARF调试数据(在mach-o和ELF二进制文件内部)和PDB调试数据。

示例

use profiler_get_symbols::{
    FileContents, FileAndPathHelper, FileAndPathHelperResult, OptionallySendFuture,
    CandidatePathInfo, FileLocation
};
use profiler_get_symbols::debugid::DebugId;

async fn run_query() -> String {
    let this_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let helper = ExampleHelper {
        artifact_directory: this_dir.join("..").join("fixtures").join("win64-ci")
    };
    profiler_get_symbols::query_api(
        "/symbolicate/v5",
        r#"{
            "memoryMap": [
              [
                "firefox.pdb",
                "AA152DEB2D9B76084C4C44205044422E1"
              ]
            ],
            "stacks": [
              [
                [0, 204776],
                [0, 129423],
                [0, 244290],
                [0, 244219]
              ]
            ]
          }"#,
        &helper,
    ).await
}

struct ExampleHelper {
    artifact_directory: std::path::PathBuf,
}

impl<'h> FileAndPathHelper<'h> for ExampleHelper {
    type F = Vec<u8>;
    type OpenFileFuture =
        std::pin::Pin<Box<dyn std::future::Future<Output = FileAndPathHelperResult<Self::F>> + 'h>>;

    fn get_candidate_paths_for_binary_or_pdb(
        &self,
        debug_name: &str,
        _debug_id: &DebugId,
    ) -> FileAndPathHelperResult<Vec<CandidatePathInfo>> {
        Ok(vec![CandidatePathInfo::SingleFile(FileLocation::Path(self.artifact_directory.join(debug_name)))])
    }

    fn open_file(
        &'h self,
        location: &FileLocation,
    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = FileAndPathHelperResult<Self::F>> + 'h>> {
        async fn read_file_impl(path: std::path::PathBuf) -> FileAndPathHelperResult<Vec<u8>> {
            Ok(std::fs::read(&path)?)
        }

        let path = match location {
            FileLocation::Path(path) => path.clone(),
            FileLocation::Custom(_) => panic!("Unexpected FileLocation::Custom"),
        };
        Box::pin(read_file_impl(path.to_path_buf()))
    }
}

依赖关系

~13MB
~239K SLoC