stylus_trace_core/parser/
source_map.rs1use addr2line::Context;
15use log::{debug, info};
16use std::path::Path;
17
18#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
20pub struct SourceLocation {
21 pub file: String,
22 pub line: Option<u32>,
23 pub column: Option<u32>,
24 pub function: Option<String>,
25}
26
27type Reader = addr2line::gimli::EndianReader<addr2line::gimli::RunTimeEndian, std::rc::Rc<[u8]>>;
28
29pub struct SourceMapper {
31 context: Option<Context<Reader>>,
32}
33
34impl SourceMapper {
35 pub fn new<P: AsRef<Path>>(wasm_path: P) -> anyhow::Result<Self> {
37 let path = wasm_path.as_ref();
38 debug!("Loading WASM binary for source mapping: {}", path.display());
39
40 let file_data = std::fs::read(path)?;
41 let obj = object::File::parse(&*file_data)?;
42
43 let context = Context::new(&obj).ok();
44
45 if context.is_none() {
46 info!("No debug information (DWARF) found in the WASM binary. Source-to-line mapping will not be available.");
47 info!("Tip: Compile your contract with `debug = true` in your Cargo.toml release profile.");
48 } else {
49 info!("Debug information loaded successfully. Source-to-line mapping enabled.");
50 }
51
52 Ok(Self { context })
53 }
54
55 pub fn empty() -> Self {
57 Self { context: None }
58 }
59
60 pub fn lookup(&self, offset: u64) -> Option<SourceLocation> {
62 let context = self.context.as_ref()?;
63
64 let mut frames = context.find_frames(offset).skip_all_loads().ok()?;
67
68 if let Some(frame) = frames.next().ok().flatten() {
69 let function = frame
70 .function
71 .and_then(|f| f.demangle().ok().map(|d| d.into_owned()));
72
73 let location = frame.location;
74
75 return Some(SourceLocation {
76 file: location
77 .as_ref()
78 .and_then(|l| l.file)
79 .map(|f| f.to_string())
80 .unwrap_or_else(|| "unknown".to_string()),
81 line: location.as_ref().and_then(|l| l.line),
82 column: location.as_ref().and_then(|l| l.column),
83 function,
84 });
85 }
86
87 None
88 }
89}