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: Vec<_> = store.modules().all_modules().cloned().collect();
43 let instances: Vec<Instance> = store.all_instances().collect();
44 let store_memories: Vec<Memory> =
45 store.all_memories().filter_map(|m| m.unshared()).collect();
46
47 let mut store_globals: Vec<Global> = vec![];
48 store.for_each_global(|_store, global| store_globals.push(global));
49
50 WasmCoreDump {
51 name: String::from("store_name"),
52 modules,
53 instances,
54 memories: store_memories,
55 globals: store_globals,
56 backtrace,
57 }
58 }
59
60 pub fn frames(&self) -> &[FrameInfo] {
65 self.backtrace.frames()
66 }
67
68 pub fn modules(&self) -> &[Module] {
71 self.modules.as_ref()
72 }
73
74 pub fn instances(&self) -> &[Instance] {
76 self.instances.as_ref()
77 }
78
79 pub fn globals(&self) -> &[Global] {
82 self.globals.as_ref()
83 }
84
85 pub fn memories(&self) -> &[Memory] {
88 self.memories.as_ref()
89 }
90
91 pub fn serialize(&self, mut store: impl AsContextMut, name: &str) -> Vec<u8> {
103 let store = store.as_context_mut();
104 self._serialize(store, name)
105 }
106
107 fn _serialize<T: 'static>(&self, mut store: StoreContextMut<'_, T>, name: &str) -> Vec<u8> {
108 let mut core_dump = wasm_encoder::Module::new();
109
110 core_dump.section(&wasm_encoder::CoreDumpSection::new(name));
111
112 let mut memory_to_idx = HashMap::new();
115
116 let mut data = wasm_encoder::DataSection::new();
117
118 {
119 let mut memories = wasm_encoder::MemorySection::new();
120 for mem in self.memories() {
121 let memory_idx = memories.len();
122 memory_to_idx.insert(mem.hash_key(&store.0), memory_idx);
123 let ty = mem.ty(&store);
124 memories.memory(wasm_encoder::MemoryType {
125 minimum: mem.size(&store),
126 maximum: ty.maximum(),
127 memory64: ty.is_64(),
128 shared: ty.is_shared(),
129 page_size_log2: None,
130 });
131
132 const CHUNK_SIZE: usize = 4096;
143 for (i, chunk) in mem.data(&store).chunks_exact(CHUNK_SIZE).enumerate() {
144 if let Some(start) = chunk.iter().position(|byte| *byte != 0) {
145 let end = chunk.iter().rposition(|byte| *byte != 0).unwrap() + 1;
146 let offset = i * CHUNK_SIZE + start;
147 let offset = if ty.is_64() {
148 let offset = u64::try_from(offset).unwrap();
149 wasm_encoder::ConstExpr::i64_const(offset as i64)
150 } else {
151 let offset = u32::try_from(offset).unwrap();
152 wasm_encoder::ConstExpr::i32_const(offset as i32)
153 };
154 data.active(memory_idx, &offset, chunk[start..end].iter().copied());
155 }
156 }
157 }
158 core_dump.section(&memories);
159 }
160
161 let mut global_to_idx = HashMap::new();
164
165 {
166 let mut globals = wasm_encoder::GlobalSection::new();
167 for g in self.globals() {
168 global_to_idx.insert(g.hash_key(&store.0), globals.len());
169 let ty = g.ty(&store);
170 let mutable = matches!(ty.mutability(), crate::Mutability::Var);
171 let val_type = match ty.content() {
172 ValType::I32 => wasm_encoder::ValType::I32,
173 ValType::I64 => wasm_encoder::ValType::I64,
174 ValType::F32 => wasm_encoder::ValType::F32,
175 ValType::F64 => wasm_encoder::ValType::F64,
176 ValType::V128 => wasm_encoder::ValType::V128,
177
178 ValType::Ref(r) => match r.heap_type().top() {
184 HeapType::Extern => wasm_encoder::ValType::EXTERNREF,
185
186 HeapType::Func => wasm_encoder::ValType::FUNCREF,
187
188 HeapType::Any => wasm_encoder::ValType::Ref(wasm_encoder::RefType::ANYREF),
189
190 ty => unreachable!("not a top type: {ty:?}"),
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) => wasm_encoder::ConstExpr::f32_const(f32::from_bits(x).into()),
197 Val::F64(x) => wasm_encoder::ConstExpr::f64_const(f64::from_bits(x).into()),
198 Val::V128(x) => wasm_encoder::ConstExpr::v128_const(x.as_u128() as i128),
199 Val::FuncRef(_) => {
200 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::FUNC)
201 }
202 Val::ExternRef(_) => {
203 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::EXTERN)
204 }
205 Val::AnyRef(_) => {
206 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::ANY)
207 }
208 Val::ExnRef(_) => {
209 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Abstract {
210 shared: false,
211 ty: wasm_encoder::AbstractHeapType::Exn,
212 })
213 }
214 Val::ContRef(_) => {
215 wasm_encoder::ConstExpr::ref_null(wasm_encoder::HeapType::Abstract {
216 shared: false,
217 ty: wasm_encoder::AbstractHeapType::Cont,
218 })
219 }
220 };
221 globals.global(
222 wasm_encoder::GlobalType {
223 val_type,
224 mutable,
225 shared: false,
226 },
227 &init,
228 );
229 }
230 core_dump.section(&globals);
231 }
232
233 core_dump.section(&data);
234 drop(data);
235
236 let mut module_to_index = HashMap::new();
239
240 {
241 let mut modules = wasm_encoder::CoreDumpModulesSection::new();
242 for module in self.modules() {
243 module_to_index.insert(module.id(), modules.len());
244 match module.name() {
245 Some(name) => modules.module(name),
246 None => modules.module(&format!("<anonymous-module-{}>", modules.len())),
247 };
248 }
249 core_dump.section(&modules);
250 }
251
252 let mut module_to_instance = HashMap::new();
259
260 {
261 let mut instances = wasm_encoder::CoreDumpInstancesSection::new();
262 for instance in self.instances() {
263 let module = instance.module(&store);
264 module_to_instance.insert(module.id(), instances.len());
265
266 let module_index = module_to_index[&module.id()];
267
268 let memories = instance
269 .all_memories(store.0)
270 .filter_map(|(_, m)| m.unshared())
271 .map(|memory| {
272 memory_to_idx
273 .get(&memory.hash_key(&store.0))
274 .copied()
275 .unwrap_or(u32::MAX)
276 })
277 .collect::<Vec<_>>();
278
279 let globals = instance
280 .all_globals(store.0)
281 .collect::<Vec<_>>()
282 .into_iter()
283 .map(|(_i, global)| global_to_idx[&global.hash_key(&store.0)])
284 .collect::<Vec<_>>();
285
286 instances.instance(module_index, memories, globals);
287 }
288 core_dump.section(&instances);
289 }
290
291 {
292 let thread_name = "main";
293 let mut stack = wasm_encoder::CoreDumpStackSection::new(thread_name);
294 for frame in self.frames() {
295 let instance = module_to_instance[&frame.module().id()];
299
300 let func = frame.func_index();
301
302 let offset = frame
303 .func_offset()
304 .and_then(|o| u32::try_from(o).ok())
305 .unwrap_or(0);
306
307 let locals = [];
310 let operand_stack = [];
311
312 stack.frame(instance, func, offset, locals, operand_stack);
313 }
314 core_dump.section(&stack);
315 }
316
317 core_dump.finish()
318 }
319}
320
321impl fmt::Display for WasmCoreDump {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 writeln!(f, "wasm coredump generated while executing {}:", self.name)?;
324 writeln!(f, "modules:")?;
325 for module in self.modules.iter() {
326 writeln!(f, " {}", module.name().unwrap_or("<module>"))?;
327 }
328
329 writeln!(f, "instances:")?;
330 for instance in self.instances.iter() {
331 writeln!(f, " {instance:?}")?;
332 }
333
334 writeln!(f, "memories:")?;
335 for memory in self.memories.iter() {
336 writeln!(f, " {memory:?}")?;
337 }
338
339 writeln!(f, "globals:")?;
340 for global in self.globals.iter() {
341 writeln!(f, " {global:?}")?;
342 }
343
344 writeln!(f, "backtrace:")?;
345 write!(f, "{}", self.backtrace)?;
346
347 Ok(())
348 }
349}
350
351impl fmt::Debug for WasmCoreDump {
352 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
353 write!(f, "<wasm core dump>")
354 }
355}