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