use anyhow::{anyhow, Result};
use std::path::Path;
pub struct DwarfResolver {
loader: addr2line::Loader,
}
impl DwarfResolver {
pub fn load(path: &Path) -> Result<Self> {
let loader = addr2line::Loader::new(path).map_err(|e| anyhow!("{e}"))?;
Ok(DwarfResolver { loader })
}
pub fn crate_for_addr(&self, addr: u64) -> Option<String> {
let loc = self.loader.find_location(addr).ok()??;
crate_from_path(loc.file?)
}
}
fn crate_from_path(path: &str) -> Option<String> {
if let Some(rest) = after(path, "/registry/src/") {
let comp = rest.split('/').nth(1)?;
return Some(strip_version(comp).replace('-', "_"));
}
if let Some(rest) = after(path, "/library/") {
if path.contains("/rustc/") || path.contains("/lib/rustlib/") {
return Some(rest.split('/').next()?.replace('-', "_"));
}
}
None
}
fn after<'a>(hay: &'a str, needle: &str) -> Option<&'a str> {
hay.find(needle).map(|i| &hay[i + needle.len()..])
}
fn strip_version(dir: &str) -> String {
let parts: Vec<&str> = dir.split('-').collect();
let cut = parts
.iter()
.position(|p| p.chars().next().is_some_and(|c| c.is_ascii_digit()))
.unwrap_or(parts.len());
if cut == 0 {
dir.to_string()
} else {
parts[..cut].join("-")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_path_to_crate() {
assert_eq!(
crate_from_path(
"/Users/x/.cargo/registry/src/index.crates.io-abc/regex-automata-0.4.3/src/dfa.rs"
),
Some("regex_automata".to_string())
);
assert_eq!(
crate_from_path("/root/.cargo/registry/src/idx/serde_json-1.0.150/src/de.rs"),
Some("serde_json".to_string())
);
}
#[test]
fn std_path_to_crate() {
assert_eq!(
crate_from_path("/rustc/abc123/library/core/src/ptr/mod.rs"),
Some("core".to_string())
);
assert_eq!(
crate_from_path("/rustc/abc/library/alloc/src/vec.rs"),
Some("alloc".to_string())
);
}
#[test]
fn local_path_is_unmapped() {
assert_eq!(crate_from_path("/home/me/project/src/main.rs"), None);
assert_eq!(crate_from_path("src/lib.rs"), None);
}
#[test]
fn version_stripping() {
assert_eq!(strip_version("regex-automata-0.4.3"), "regex-automata");
assert_eq!(strip_version("serde_json-1.0.150"), "serde_json");
assert_eq!(strip_version("rustc-demangle-0.1.27"), "rustc-demangle");
assert_eq!(strip_version("noversion"), "noversion");
}
}