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 mut store_memories: Vec<Memory> = store.all_memories().collect();
45 store_memories.retain(|m| !m.wasmtime_ty(store).shared);
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 .collect::<Vec<_>>()
271 .into_iter()
272 .map(|(_i, memory)| {
273 memory_to_idx
274 .get(&memory.hash_key(&store.0))
275 .copied()
276 .unwrap_or(u32::MAX)
277 })
278 .collect::<Vec<_>>();
279
280 let globals = instance
281 .all_globals(store.0)
282 .collect::<Vec<_>>()
283 .into_iter()
284 .map(|(_i, global)| global_to_idx[&global.hash_key(&store.0)])
285 .collect::<Vec<_>>();
286
287 instances.instance(module_index, memories, globals);
288 }
289 core_dump.section(&instances);
290 }
291
292 {
293 let thread_name = "main";
294 let mut stack = wasm_encoder::CoreDumpStackSection::new(thread_name);
295 for frame in self.frames() {
296 let instance = module_to_instance[&frame.module().id()];
300
301 let func = frame.func_index();
302
303 let offset = frame
304 .func_offset()
305 .and_then(|o| u32::try_from(o).ok())
306 .unwrap_or(0);
307
308 let locals = [];
311 let operand_stack = [];
312
313 stack.frame(instance, func, offset, locals, operand_stack);
314 }
315 core_dump.section(&stack);
316 }
317
318 core_dump.finish()
319 }
320}
321
322impl fmt::Display for WasmCoreDump {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324 writeln!(f, "wasm coredump generated while executing {}:", self.name)?;
325 writeln!(f, "modules:")?;
326 for module in self.modules.iter() {
327 writeln!(f, " {}", module.name().unwrap_or("<module>"))?;
328 }
329
330 writeln!(f, "instances:")?;
331 for instance in self.instances.iter() {
332 writeln!(f, " {instance:?}")?;
333 }
334
335 writeln!(f, "memories:")?;
336 for memory in self.memories.iter() {
337 writeln!(f, " {memory:?}")?;
338 }
339
340 writeln!(f, "globals:")?;
341 for global in self.globals.iter() {
342 writeln!(f, " {global:?}")?;
343 }
344
345 writeln!(f, "backtrace:")?;
346 write!(f, "{}", self.backtrace)?;
347
348 Ok(())
349 }
350}
351
352impl fmt::Debug for WasmCoreDump {
353 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354 write!(f, "<wasm core dump>")
355 }
356}