samply-symbols

This crate allows obtaining symbol information from binaries and compilation artifacts. It maps raw code addresses to symbol strings, and, if available, file name + line number information. The API was designed for the Firefox profiler.

The main entry point of this crate is the async get_symbolication_result function.

Design constraints

This crate operates under the following design constraints:

The WebAssembly requirement means that this crate cannot contain any direct file access. Instead, all file access is mediated through a FileAndPathHelper trait which has to be implemented by the caller. Furthermore, the API request does not carry any absolute file paths, so the resolution to absolute file paths needs to be done by the caller as well.

Supported formats and data

This crate supports obtaining symbol data from PE binaries (Windows), PDB files (Windows), mach-o binaries (including fat binaries) (macOS & iOS), and ELF binaries (Linux, Android, etc.). For mach-o files it also supports finding debug information in external objects, by following OSO stabs entries. It supports gathering both basic symbol information (function name strings) as well as information based on debug data, i.e. inline callstacks where each frame has a function name, a file name, and a line number. For debug data we support both DWARF debug data (inside mach-o and ELF binaries) and PDB debug data.

Example

```rust use samplysymbols::{ FileContents, FileAndPathHelper, FileAndPathHelperResult, OptionallySendFuture, CandidatePathInfo, FileLocation, AddressDebugInfo, SymbolicationResult, SymbolicationResultKind, SymbolicationQuery }; use samplysymbols::debugid::DebugId;

async fn runquery() -> String { let thisdir = std::path::PathBuf::from(env!("CARGOMANIFESTDIR")); let helper = ExampleHelper { artifactdirectory: thisdir.join("..").join("fixtures").join("win64-ci") }; let r: Result = samplysymbols::getsymbolicationresult( SymbolicationQuery { debugname: "firefox.pdb", debugid: DebugId::frombreakpad("AA152DEB2D9B76084C4C44205044422E1").unwrap(), resultkind: SymbolicationResultKind::SymbolsForAddresses { addresses: &[204776, 129423, 244290, 244219], withdebug_info: true, }, }, &helper, ).await; match r { Ok(res) => format!("{:?}", res), Err(err) => format!("Error: {}", err), } }

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

impl<'h> FileAndPathHelper<'h> for ExampleHelper { type F = Vec; type OpenFileFuture = std::pin::Pin> + '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()))
}

}

[derive(Debug, Default)]

struct ExampleSymbolicationResult { /// For each address, the symbol name and the (optional) debug info. map: std::collections::HashMap)>, }

impl SymbolicationResult for ExampleSymbolicationResult { fn fromfullmap(_map: Vec<(u32, S)>) -> Self where S: std::ops::Deref, { panic!("Should not be called") }

fn for_addresses(_addresses: &[u32]) -> Self {
    Default::default()
}

fn add_address_symbol(
    &mut self,
    address: u32,
    _symbol_address: u32,
    symbol_name: String,
    _function_size: Option<u32>,
) {
    self.map.insert(address, (symbol_name, None));
}

fn add_address_debug_info(&mut self, address: u32, info: AddressDebugInfo) {
    if let Some(entry) = self.map.get_mut(&address) {
        entry.1 = Some(info);
    }
}

fn set_total_symbol_count(&mut self, _total_symbol_count: u32) {
    // ignored
}

} ```