1use std::{collections::HashMap, fmt};
2
3use crate::{
4 store::StoreOpaque, AsContextMut, FrameInfo, Global, HeapType, Instance, Memory, Module,
5 StoreContextMut, Val, ValType, WasmBacktrace,
6};
7
8pub struct WasmCoreDump {
31 name: String,
32 modules: Vec<Module>,
33 instances: Vec<Instance>,
34 memories: Vec<Memory>,
35 globals: Vec<Global>,
36 backtrace: WasmBacktrace,
37}
38
39impl WasmCoreDump {
40 pub(crate) fn new(store: &mut StoreOpaque, backtrace: WasmBacktrace) -> WasmCoreDump {
41 let modules: Vec<_> = store.modules().all_modules().cloned().collect();
42 let instances: Vec<Instance> = store.all_instances().collect();
43 let store_memories: Vec<Memory> = store.all_memories().collect();
44
45 let mut store_globals: Vec<Global> = vec![];
46 store.for_each_global(|_store, global| store_globals.push(global));
47
48 WasmCoreDump {
49 name: String::from("store_name"),
50 modules,
51 instances,
52 memories: store_memories,
53 globals: store_globals,
54 backtrace,
55 }
56 }
57
58 pub fn frames(&self) -> &[FrameInfo] {
63 self.backtrace.frames()
64 }
65
66 pub fn modules(&self) -> &[Module] {
69 self.modules.as_ref()
70 }
71
72 pub fn instances(&self) -> &[Instance] {
74 self.instances.as_ref()
75 }
76
77 pub fn globals(&self) -> &[Global] {
80 self.globals.as_ref()
81 }
82
83 pub fn memories(&self) -> &[Memory] {
86 self.memories.as_ref()
87 }
88
89 pub fn serialize(&self, mut store: impl AsContextMut, name: &str) -> Vec<u8> {
101 let store = store.as_context_mut();
102 self._serialize(store, name)
103 }
104
105 fn _serialize<T>(&self, mut store: StoreContextMut<'_, T>, name: &str) -> Vec<u8> {
106 let mut core_dump = wasm_encoder::Module::new();
107
108 core_dump.section(&wasm_encoder::CoreDumpSection::new(name));
109
110 let mut memory_to_idx = HashMap::new();
113
114 let mut data = wasm_encoder::DataSection::new();
115
116 {
117 let mut memories = wasm_encoder::MemorySection::new();
118 for mem in self.memories() {
119 let memory_idx = memories.len();
120 memory_to_idx.insert(mem.hash_key(&store.0), memory_idx);
121 let ty = mem.ty(&store);
122 memories.memory(wasm_encoder::MemoryType {
123 minimum: mem.size(&store),
124 maximum: ty.maximum(),
125 memory64: ty.is_64(),
126 shared: ty.is_shared(),
127 });
128
129 const CHUNK_SIZE: u32 = 4096;
140 for (i, chunk) in mem
141 .data(&store)
142 .chunks_exact(CHUNK_SIZE as usize)
143 .enumerate()
144 {
145 if let Some(start) = chunk.iter().position(|byte| *byte != 0) {
146 let end = chunk.iter().rposition(|byte| *byte != 0).unwrap() + 1;
147 let offset = (i as u32) * CHUNK_SIZE + (start as u32);
148 let offset = wasm_encoder::ConstExpr::i32_const(offset as i32);
149 data.active(memory_idx, &offset, chunk[start..end].iter().copied());
150 }
151 }
152 }
153 core_dump.section(&memories);
154 }
155
156 let mut global_to_idx = HashMap::new();
159
160 {
161 let mut globals = wasm_encoder::GlobalSection::new();
162 for g in self.globals() {
163 global_to_idx.insert(g.hash_key(&store.0), globals.len());
164 let ty = g.ty(&store);
165 let mutable = matches!(ty.mutability(), crate::Mutability::Var);
166 let val_type = match ty.content() {
167 ValType::I32 => wasm_encoder::ValType::I32,
168 ValType::I64 => wasm_encoder::ValType::I64,
169 ValType::F32 => wasm_encoder::ValType::F32,
170 ValType::F64 => wasm_encoder::ValType::F64,
171 ValType::V128 => wasm_encoder::ValType::V128,
172
173 ValType::Ref(r) => match r.heap_type() {
179 HeapType::Extern => wasm_encoder::ValType::EXTERNREF,
180
181 HeapType::Func | HeapType::Concrete(_) | HeapType::NoFunc => {
182 wasm_encoder::ValType::FUNCREF
183 }
184
185 HeapType::Any | HeapType::I31 | HeapType::None => {
186 wasm_encoder::ValType::Ref(wasm_encoder::RefType {
187 nullable: true,
188 heap_type: wasm_encoder::HeapType::Any,
189 })
190 }
191 },
192 };
193 let init = match g.get(&mut store) {
194 Val::I32(x) => wasm_encoder::ConstExpr::i32_const(x),
195 Val::I64(x) => wasm_encoder::ConstExpr::i64_const(x),
196 Val::F32(x) => {
197 wasm_encoder::ConstExpr::f32_const(unsafe { std::mem::transmute(x) })
198 }
199 Val::F64(x) => {
200 wasm_encoder::ConstExpr::f64_const(unsafe { std::mem::transmute(x) })
201 }
202 Val::V128(x) => wasm_encoder::ConstExpr::v128_const(x.as_u128() as i128),
203 Val::FuncRef(_) => {
204 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Func)
205 }
206 Val::ExternRef(_) => {
207 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Extern)
208 }
209 Val::AnyRef(_) => {
210 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Any)
211 }
212 };
213 globals.global(wasm_encoder::GlobalType { val_type, mutable }, &init);
214 }
215 core_dump.section(&globals);
216 }
217
218 core_dump.section(&data);
219 drop(data);
220
221 let mut module_to_index = HashMap::new();
224
225 {
226 let mut modules = wasm_encoder::CoreDumpModulesSection::new();
227 for module in self.modules() {
228 module_to_index.insert(module.id(), modules.len());
229 match module.name() {
230 Some(name) => modules.module(name),
231 None => modules.module(&format!("<anonymous-module-{}>", modules.len())),
232 };
233 }
234 core_dump.section(&modules);
235 }
236
237 let mut module_to_instance = HashMap::new();
244
245 {
246 let mut instances = wasm_encoder::CoreDumpInstancesSection::new();
247 for instance in self.instances() {
248 let module = instance.module(&store);
249 module_to_instance.insert(module.id(), instances.len());
250
251 let module_index = module_to_index[&module.id()];
252
253 let memories = instance
254 .all_memories(&mut store.0)
255 .collect::<Vec<_>>()
256 .into_iter()
257 .map(|(_i, memory)| memory_to_idx[&memory.hash_key(&store.0)])
258 .collect::<Vec<_>>();
259
260 let globals = instance
261 .all_globals(&mut store.0)
262 .collect::<Vec<_>>()
263 .into_iter()
264 .map(|(_i, global)| global_to_idx[&global.hash_key(&store.0)])
265 .collect::<Vec<_>>();
266
267 instances.instance(module_index, memories, globals);
268 }
269 core_dump.section(&instances);
270 }
271
272 {
273 let thread_name = "main";
274 let mut stack = wasm_encoder::CoreDumpStackSection::new(thread_name);
275 for frame in self.frames() {
276 let instance = module_to_instance[&frame.module().id()];
280
281 let func = frame.func_index();
282
283 let offset = frame
284 .func_offset()
285 .and_then(|o| u32::try_from(o).ok())
286 .unwrap_or(0);
287
288 let locals = [];
291 let operand_stack = [];
292
293 stack.frame(instance, func, offset, locals, operand_stack);
294 }
295 core_dump.section(&stack);
296 }
297
298 core_dump.finish()
299 }
300}
301
302impl fmt::Display for WasmCoreDump {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 writeln!(f, "wasm coredump generated while executing {}:", self.name)?;
305 writeln!(f, "modules:")?;
306 for module in self.modules.iter() {
307 writeln!(f, " {}", module.name().unwrap_or("<module>"))?;
308 }
309
310 writeln!(f, "instances:")?;
311 for instance in self.instances.iter() {
312 writeln!(f, " {:?}", instance)?;
313 }
314
315 writeln!(f, "memories:")?;
316 for memory in self.memories.iter() {
317 writeln!(f, " {:?}", memory)?;
318 }
319
320 writeln!(f, "globals:")?;
321 for global in self.globals.iter() {
322 writeln!(f, " {:?}", global)?;
323 }
324
325 writeln!(f, "backtrace:")?;
326 write!(f, "{}", self.backtrace)?;
327
328 Ok(())
329 }
330}
331
332impl fmt::Debug for WasmCoreDump {
333 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334 write!(f, "<wasm core dump>")
335 }
336}