1use rustc_demangle::demangle;
2use std::collections::HashMap;
3use std::sync::Arc;
4use std::sync::Mutex;
5use wasmgdb_ddbug_parser as ddbug_parser;
6
7type BoxError = Box<dyn std::error::Error>;
8
9#[derive(Debug)]
10pub struct Frame {
11 pub name: String,
12 pub location: FrameLocation,
13}
14
15#[derive(Debug)]
16pub struct FrameLocation {
17 pub file: String,
18 pub line: u32,
19}
20
21impl FrameLocation {
22 fn unknown() -> Self {
23 Self {
24 file: "unknown.rs".to_owned(),
25 line: 0,
26 }
27 }
28}
29
30pub struct CoredumpToStack {
31 coredump: core_wasm_ast::Module,
32
33 func_names: Option<HashMap<u32, String>>,
35
36 debug_module: Option<Vec<u8>>,
38}
39
40impl CoredumpToStack {
41 pub fn new(coredump_bytes: &[u8]) -> Result<Self, BoxError> {
42 let coredump = wasm_parser::parse(coredump_bytes)
43 .map_err(|err| format!("failed to parse Wasm module: {}", err))?;
44
45 Ok(Self {
46 coredump,
47 func_names: None,
48 debug_module: None,
49 })
50 }
51
52 pub fn with_debug_sections(
53 self,
54 sections: HashMap<&'static str, Vec<u8>>,
55 ) -> Result<Self, BoxError> {
56 let mut debug_module = vec![];
57 wasm_printer::wasm::write_header(&mut debug_module)
58 .map_err(|err| format!("failed to write header: {err}"))?;
59
60 let name_section_bytes = sections
61 .get("name")
62 .ok_or::<BoxError>("missing function names in name section".into())?;
63 let name_section = wasm_parser::parse_custom_section_name(name_section_bytes)?;
64 let func_names = name_section
65 .func_names
66 .ok_or::<BoxError>("missing function names in name section".into())?;
67 let func_names = func_names.lock().unwrap();
68
69 for (k, v) in sections {
70 if k == "name" {
71 continue;
79 }
80
81 let custom_section = core_wasm_ast::CustomSection::Unknown(k.to_owned(), v);
85 let section_size = core_wasm_ast::Value::new(0);
87 let section = core_wasm_ast::Section::Custom((
88 section_size,
89 Arc::new(Mutex::new(custom_section)),
90 ));
91 wasm_printer::wasm::write_section(&mut debug_module, §ion)
92 .map_err(|err| format!("failed to write custom section {k}: {err}"))?;
93 }
94
95 Ok(Self {
96 coredump: self.coredump,
97 func_names: Some(func_names.clone()),
98 debug_module: Some(debug_module.to_owned()),
99 })
100 }
101
102 pub fn with_debug_module(self, bytes: &[u8]) -> Result<Self, BoxError> {
103 let module = wasm_parser::parse(&bytes)
104 .map_err(|err| format!("failed to parse Wasm module: {}", err))?;
105 let module = core_wasm_ast::traverse::WasmModule::new(Arc::new(module));
106
107 let func_names = module.func_names.lock().unwrap();
108
109 Ok(Self {
110 coredump: self.coredump,
111 func_names: Some(func_names.clone()),
112 debug_module: Some(bytes.to_owned()),
113 })
114 }
115
116 pub fn stack(self) -> Result<Vec<Frame>, BoxError> {
117 let coredump_wasm = core_wasm_ast::traverse::WasmModule::new(Arc::new(self.coredump));
118
119 let func_names = self
120 .func_names
121 .ok_or::<BoxError>("missing name section".into())?;
122 let coredump = coredump_wasm.get_coredump()?;
123
124 let mut out_frames = vec![];
125
126 let arena = ddbug_parser::Arena::new();
127 #[allow(unused_assignments)]
128 let mut file = None;
131
132 let functions_by_linkage_name = if let Some(debug_module) = &self.debug_module {
133 let object = object::read::File::parse(debug_module.as_slice()).unwrap();
134 file = Some(
135 ddbug_parser::File::parse_object(
136 &object,
137 &object,
138 "module.wasm".to_owned(),
139 &arena,
140 )
141 .unwrap(),
142 );
143 let mut ddbug = ddbug_parser::FileHash::new(&file.as_ref().unwrap());
144
145 let mut new = HashMap::new();
146
147 for (k, v) in ddbug.functions_by_linkage_name.iter() {
150 new.insert(demangle(&k).to_string(), v.clone());
151 }
152
153 ddbug.functions_by_linkage_name.extend(new);
154 Some(ddbug.functions_by_linkage_name)
155 } else {
156 None
159 };
160
161 let mut frames = coredump.stacks[0].frames.clone();
162 frames.reverse();
163
164 for frame in frames {
165 let linkage_name = func_names
166 .get(&frame.funcidx)
167 .unwrap_or(&format!("<unknown-func{}>", frame.funcidx))
168 .to_owned();
169
170 if let Some(functions_by_linkage_name) = &functions_by_linkage_name {
171 if let Some(function) = functions_by_linkage_name.get(&linkage_name) {
172 let mut name = "".to_owned();
173
174 if let Some(ns) = function.namespace() {
175 name += &format!("{}::", ns.name().unwrap());
176 }
177 name += function.name().unwrap_or(&linkage_name);
178
179 let file = format!(
180 "{}/{}",
181 function.source().directory().unwrap_or(""),
182 function.source().file().unwrap_or("unknown.rs")
183 );
184
185 let location = FrameLocation {
186 file,
187 line: function.source().line(),
188 };
189
190 out_frames.push(Frame { name, location })
191 } else {
192 let location = FrameLocation::unknown();
193 out_frames.push(Frame {
194 name: linkage_name,
195 location,
196 })
197 }
198 } else {
199 let location = FrameLocation::unknown();
200 out_frames.push(Frame {
201 name: linkage_name,
202 location,
203 })
204 }
205 }
206
207 Ok(out_frames)
208 }
209}