1use crate::hash_map::HashMap;
2use crate::prelude::*;
3use crate::{
4 AsContextMut, FrameInfo, Global, HeapType, Instance, Memory, Module, StoreContextMut, Val,
5 ValType, WasmBacktrace, store::StoreOpaque,
6};
7use std::fmt;
8
9pub struct WasmCoreDump {
32 name: String,
33 modules: Vec<Module>,
34 instances: Vec<Instance>,
35 memories: Vec<Memory>,
36 globals: Vec<Global>,
37 backtrace: WasmBacktrace,
38}
39
40impl WasmCoreDump {
41 pub(crate) fn new(store: &mut StoreOpaque, backtrace: WasmBacktrace) -> WasmCoreDump {
42 let modules = store
43 .modules()
44 .all_modules()
45 .map(|(_, m)| m.clone())
46 .collect::<Vec<_>>();
47 let instances: Vec<Instance> = store.all_instances().collect();
48 let store_memories: Vec<Memory> =
49 store.all_memories().filter_map(|m| m.unshared()).collect();
50
51 let mut store_globals: Vec<Global> = vec![];
52 store.for_each_global(|_store, global| store_globals.push(global));
53
54 WasmCoreDump {
55 name: String::from("store_name"),
56 modules,
57 instances,
58 memories: store_memories,
59 globals: store_globals,
60 backtrace,
61 }
62 }
63
64 pub fn frames(&self) -> &[FrameInfo] {
69 self.backtrace.frames()
70 }
71
72 pub fn modules(&self) -> &[Module] {
75 self.modules.as_ref()
76 }
77
78 pub fn instances(&self) -> &[Instance] {
80 self.instances.as_ref()
81 }
82
83 pub fn globals(&self) -> &[Global] {
86 self.globals.as_ref()
87 }
88
89 pub fn memories(&self) -> &[Memory] {
92 self.memories.as_ref()
93 }
94
95 pub fn serialize(&self, mut store: impl AsContextMut, name: &str) -> Vec<u8> {
107 let store = store.as_context_mut();
108 self._serialize(store, name)
109 }
110
111 fn _serialize<T: 'static>(&self, mut store: StoreContextMut<'_, T>, name: &str) -> Vec<u8> {
112 let mut core_dump = wasm_encoder::Module::new();
113
114 core_dump.section(&wasm_encoder::CoreDumpSection::new(name));
115
116 let mut memory_to_idx = HashMap::new();
119
120 let mut data = wasm_encoder::DataSection::new();
121
122 {
123 let mut memories = wasm_encoder::MemorySection::new();
124 for mem in self.memories() {
125 let memory_idx = memories.len();
126 memory_to_idx.insert(mem.hash_key(&store.0), memory_idx);
127 let ty = mem.ty(&store);
128 memories.memory(wasm_encoder::MemoryType {
129 minimum: mem.size(&store),
130 maximum: ty.maximum(),
131 memory64: ty.is_64(),
132 shared: ty.is_shared(),
133 page_size_log2: None,
134 });
135
136 const CHUNK_SIZE: usize = 4096;
147 for (i, chunk) in mem.data(&store).chunks_exact(CHUNK_SIZE).enumerate() {
148 if let Some(start) = chunk.iter().position(|byte| *byte != 0) {
149 let end = chunk.iter().rposition(|byte| *byte != 0).unwrap() + 1;
150 let offset = i * CHUNK_SIZE + start;
151 let offset = if ty.is_64() {
152 let offset = u64::try_from(offset).unwrap();
153 wasm_encoder::ConstExpr::i64_const(offset as i64)
154 } else {
155 let offset = u32::try_from(offset).unwrap();
156 wasm_encoder::ConstExpr::i32_const(offset as i32)
157 };
158 data.active(memory_idx, &offset, chunk[start..end].iter().copied());
159 }
160 }
161 }
162 core_dump.section(&memories);
163 }
164
165 let mut global_to_idx = HashMap::new();
168
169 {
170 let mut globals = wasm_encoder::GlobalSection::new();
171 for g in self.globals() {
172 global_to_idx.insert(g.hash_key(&store.0), globals.len());
173 let ty = g.ty(&store);
174 let mutable = matches!(ty.mutability(), crate::Mutability::Var);
175 let val_type = match ty.content() {
176 ValType::I32 => wasm_encoder::ValType::I32,
177 ValType::I64 => wasm_encoder::ValType::I64,
178 ValType::F32 => wasm_encoder::ValType::F32,
179 ValType::F64 => wasm_encoder::ValType::F64,
180 ValType::V128 => wasm_encoder::ValType::V128,
181
182 ValType::Ref(r) => match r.heap_type().top() {
188 HeapType::Extern => wasm_encoder::ValType::EXTERNREF,
189
190 HeapType::Func => wasm_encoder::ValType::FUNCREF,
191
192 HeapType::Any => wasm_encoder::ValType::Ref(wasm_encoder::RefType::ANYREF),
193
194 ty => unreachable!("not a top type: {ty:?}"),
195 },
196 };
197 let init = match g.get(&mut store) {
198 Val::I32(x) => wasm_encoder::ConstExpr::i32_const(x),
199 Val::I64(x) => wasm_encoder::ConstExpr::i64_const(x),
200 Val::F32(x) => wasm_encoder::ConstExpr::f32_const(f32::from_bits(x).into()),
201 Val::F64(x) => wasm_encoder::ConstExpr::f64_const(f64::from_bits(x).into()),
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 Val::ExnRef(_) => {
213 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Abstract {
214 shared: false,
215 ty: wasm_encoder::AbstractHeapType::Exn,
216 })
217 }
218 Val::ContRef(_) => {
219 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Abstract {
220 shared: false,
221 ty: wasm_encoder::AbstractHeapType::Cont,
222 })
223 }
224 };
225 globals.global(
226 wasm_encoder::GlobalType {
227 val_type,
228 mutable,
229 shared: false,
230 },
231 &init,
232 );
233 }
234 core_dump.section(&globals);
235 }
236
237 core_dump.section(&data);
238 drop(data);
239
240 let mut module_to_index = HashMap::new();
243
244 {
245 let mut modules = wasm_encoder::CoreDumpModulesSection::new();
246 for module in self.modules() {
247 module_to_index.insert(module.id(), modules.len());
248 match module.name() {
249 Some(name) => modules.module(name),
250 None => modules.module(&format!("<anonymous-module-{}>", modules.len())),
251 };
252 }
253 core_dump.section(&modules);
254 }
255
256 let mut module_to_instance = HashMap::new();
263
264 {
265 let mut instances = wasm_encoder::CoreDumpInstancesSection::new();
266 for instance in self.instances() {
267 let module = instance.module(&store);
268 module_to_instance.insert(module.id(), instances.len());
269
270 let module_index = module_to_index[&module.id()];
271
272 let memories = instance
273 .all_memories(store.0)
274 .filter_map(|(_, m)| m.unshared())
275 .map(|memory| {
276 memory_to_idx
277 .get(&memory.hash_key(&store.0))
278 .copied()
279 .unwrap_or(u32::MAX)
280 })
281 .collect::<Vec<_>>();
282
283 let globals = instance
284 .all_globals(store.0)
285 .collect::<Vec<_>>()
286 .into_iter()
287 .map(|(_i, global)| global_to_idx[&global.hash_key(&store.0)])
288 .collect::<Vec<_>>();
289
290 instances.instance(module_index, memories, globals);
291 }
292 core_dump.section(&instances);
293 }
294
295 {
296 let thread_name = "main";
297 let mut stack = wasm_encoder::CoreDumpStackSection::new(thread_name);
298 for frame in self.frames() {
299 let instance = module_to_instance[&frame.module().id()];
303
304 let func = frame.func_index();
305
306 let offset = frame
307 .func_offset()
308 .and_then(|o| u32::try_from(o).ok())
309 .unwrap_or(0);
310
311 let locals = [];
314 let operand_stack = [];
315
316 stack.frame(instance, func, offset, locals, operand_stack);
317 }
318 core_dump.section(&stack);
319 }
320
321 core_dump.finish()
322 }
323}
324
325impl fmt::Display for WasmCoreDump {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 writeln!(f, "wasm coredump generated while executing {}:", self.name)?;
328 writeln!(f, "modules:")?;
329 for module in self.modules.iter() {
330 writeln!(f, " {}", module.name().unwrap_or("<module>"))?;
331 }
332
333 writeln!(f, "instances:")?;
334 for instance in self.instances.iter() {
335 writeln!(f, " {instance:?}")?;
336 }
337
338 writeln!(f, "memories:")?;
339 for memory in self.memories.iter() {
340 writeln!(f, " {memory:?}")?;
341 }
342
343 writeln!(f, "globals:")?;
344 for global in self.globals.iter() {
345 writeln!(f, " {global:?}")?;
346 }
347
348 writeln!(f, "backtrace:")?;
349 write!(f, "{}", self.backtrace)?;
350
351 Ok(())
352 }
353}
354
355impl fmt::Debug for WasmCoreDump {
356 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357 write!(f, "<wasm core dump>")
358 }
359}