1use crate::{CustomSection, Encode, Section};
2use alloc::borrow::Cow;
3use alloc::string::String;
4use alloc::vec;
5use alloc::vec::Vec;
6
7#[derive(Clone, Debug, Default)]
50pub struct CoreDumpSection {
51 name: String,
52}
53
54impl CoreDumpSection {
55 pub fn new(name: impl Into<String>) -> Self {
57 let name = name.into();
58 CoreDumpSection { name }
59 }
60
61 fn as_custom<'a>(&'a self) -> CustomSection<'a> {
63 let mut data = vec![0];
64 self.name.encode(&mut data);
65 CustomSection {
66 name: "core".into(),
67 data: Cow::Owned(data),
68 }
69 }
70}
71
72impl Encode for CoreDumpSection {
73 fn encode(&self, sink: &mut Vec<u8>) {
74 self.as_custom().encode(sink);
75 }
76}
77
78impl Section for CoreDumpSection {
79 fn id(&self) -> u8 {
80 crate::core::SectionId::Custom as u8
81 }
82}
83
84#[derive(Debug)]
97pub struct CoreDumpModulesSection {
98 num_added: u32,
99 bytes: Vec<u8>,
100}
101
102impl CoreDumpModulesSection {
103 pub fn new() -> Self {
105 CoreDumpModulesSection {
106 bytes: vec![],
107 num_added: 0,
108 }
109 }
110
111 pub fn as_custom(&self) -> CustomSection<'_> {
113 let mut data = vec![];
114 self.num_added.encode(&mut data);
115 data.extend(self.bytes.iter().copied());
116 CustomSection {
117 name: "coremodules".into(),
118 data: Cow::Owned(data),
119 }
120 }
121
122 pub fn module(&mut self, module_name: impl AsRef<str>) -> &mut Self {
124 self.bytes.push(0x0);
125 module_name.as_ref().encode(&mut self.bytes);
126 self.num_added += 1;
127 self
128 }
129
130 pub fn len(&self) -> u32 {
132 self.num_added
133 }
134}
135
136impl Encode for CoreDumpModulesSection {
137 fn encode(&self, sink: &mut Vec<u8>) {
138 self.as_custom().encode(sink);
139 }
140}
141
142impl Section for CoreDumpModulesSection {
143 fn id(&self) -> u8 {
144 crate::core::SectionId::Custom as u8
145 }
146}
147
148#[derive(Debug)]
150pub struct CoreDumpInstancesSection {
151 num_added: u32,
152 bytes: Vec<u8>,
153}
154
155impl CoreDumpInstancesSection {
156 pub fn new() -> Self {
158 CoreDumpInstancesSection {
159 bytes: vec![],
160 num_added: 0,
161 }
162 }
163
164 pub fn as_custom(&self) -> CustomSection<'_> {
166 let mut data = vec![];
167 self.num_added.encode(&mut data);
168 data.extend(self.bytes.iter().copied());
169 CustomSection {
170 name: "coreinstances".into(),
171 data: Cow::Owned(data),
172 }
173 }
174
175 pub fn instance<M, G>(&mut self, module_index: u32, memories: M, globals: G) -> &mut Self
177 where
178 M: IntoIterator<Item = u32>,
179 <M as IntoIterator>::IntoIter: ExactSizeIterator,
180 G: IntoIterator<Item = u32>,
181 <G as IntoIterator>::IntoIter: ExactSizeIterator,
182 {
183 self.bytes.push(0x0);
184 module_index.encode(&mut self.bytes);
185 crate::encode_vec(memories, &mut self.bytes);
186 crate::encode_vec(globals, &mut self.bytes);
187 self.num_added += 1;
188 self
189 }
190
191 pub fn len(&self) -> u32 {
193 self.num_added
194 }
195}
196
197impl Encode for CoreDumpInstancesSection {
198 fn encode(&self, sink: &mut Vec<u8>) {
199 self.as_custom().encode(sink);
200 }
201}
202
203impl Section for CoreDumpInstancesSection {
204 fn id(&self) -> u8 {
205 crate::core::SectionId::Custom as u8
206 }
207}
208
209#[derive(Clone, Debug, Default)]
229pub struct CoreDumpStackSection {
230 frame_bytes: Vec<u8>,
231 count: u32,
232 name: String,
233}
234
235impl CoreDumpStackSection {
236 pub fn new(name: impl Into<String>) -> Self {
238 let name = name.into();
239 CoreDumpStackSection {
240 frame_bytes: Vec::new(),
241 count: 0,
242 name,
243 }
244 }
245
246 pub fn frame<L, S>(
248 &mut self,
249 instanceidx: u32,
250 funcidx: u32,
251 codeoffset: u32,
252 locals: L,
253 stack: S,
254 ) -> &mut Self
255 where
256 L: IntoIterator<Item = CoreDumpValue>,
257 <L as IntoIterator>::IntoIter: ExactSizeIterator,
258 S: IntoIterator<Item = CoreDumpValue>,
259 <S as IntoIterator>::IntoIter: ExactSizeIterator,
260 {
261 self.count += 1;
262 self.frame_bytes.push(0);
263 instanceidx.encode(&mut self.frame_bytes);
264 funcidx.encode(&mut self.frame_bytes);
265 codeoffset.encode(&mut self.frame_bytes);
266 crate::encode_vec(locals, &mut self.frame_bytes);
267 crate::encode_vec(stack, &mut self.frame_bytes);
268 self
269 }
270
271 pub fn as_custom<'a>(&'a self) -> CustomSection<'a> {
273 let mut data = vec![0];
274 self.name.encode(&mut data);
275 self.count.encode(&mut data);
276 data.extend(&self.frame_bytes);
277 CustomSection {
278 name: "corestack".into(),
279 data: Cow::Owned(data),
280 }
281 }
282}
283
284impl Encode for CoreDumpStackSection {
285 fn encode(&self, sink: &mut Vec<u8>) {
286 self.as_custom().encode(sink);
287 }
288}
289
290impl Section for CoreDumpStackSection {
291 fn id(&self) -> u8 {
292 crate::core::SectionId::Custom as u8
293 }
294}
295
296#[derive(Clone, Debug)]
300pub enum CoreDumpValue {
301 Missing,
303 I32(i32),
305 I64(i64),
307 F32(f32),
309 F64(f64),
311}
312
313impl Encode for CoreDumpValue {
314 fn encode(&self, sink: &mut Vec<u8>) {
315 match self {
316 CoreDumpValue::Missing => sink.push(0x01),
317 CoreDumpValue::I32(x) => {
318 sink.push(0x7F);
319 x.encode(sink);
320 }
321 CoreDumpValue::I64(x) => {
322 sink.push(0x7E);
323 x.encode(sink);
324 }
325 CoreDumpValue::F32(x) => {
326 sink.push(0x7D);
327 x.encode(sink);
328 }
329 CoreDumpValue::F64(x) => {
330 sink.push(0x7C);
331 x.encode(sink);
332 }
333 }
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use crate::Module;
341 use wasmparser::{KnownCustom, Parser, Payload};
342
343 #[test]
346 fn test_roundtrip_core() {
347 let core = CoreDumpSection::new("test.wasm");
348 let mut module = Module::new();
349 module.section(&core);
350
351 let wasm_bytes = module.finish();
352
353 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
354 match parser.next() {
355 Some(Ok(Payload::Version { .. })) => {}
356 _ => panic!(""),
357 }
358
359 let payload = parser
360 .next()
361 .expect("parser is not empty")
362 .expect("element is a payload");
363 match payload {
364 Payload::CustomSection(section) => {
365 assert_eq!(section.name(), "core");
366 let core = match section.as_known() {
367 KnownCustom::CoreDump(s) => s,
368 _ => panic!("not coredump"),
369 };
370 assert_eq!(core.name, "test.wasm");
371 }
372 _ => panic!("unexpected payload"),
373 }
374 }
375
376 #[test]
377 fn test_roundtrip_coremodules() {
378 let mut coremodules = CoreDumpModulesSection::new();
379 coremodules.module("test_module");
380
381 let mut module = crate::Module::new();
382 module.section(&coremodules);
383
384 let wasm_bytes = module.finish();
385
386 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
387 match parser.next() {
388 Some(Ok(Payload::Version { .. })) => {}
389 _ => panic!(""),
390 }
391
392 let payload = parser
393 .next()
394 .expect("parser is not empty")
395 .expect("element is a payload");
396 match payload {
397 Payload::CustomSection(section) => {
398 assert_eq!(section.name(), "coremodules");
399 let modules = match section.as_known() {
400 KnownCustom::CoreDumpModules(s) => s,
401 _ => panic!("not coremodules"),
402 };
403 assert_eq!(modules.modules[0], "test_module");
404 }
405 _ => panic!("unexpected payload"),
406 }
407 }
408
409 #[test]
410 fn test_roundtrip_coreinstances() {
411 let mut coreinstances = CoreDumpInstancesSection::new();
412 let module_index = 0;
413 let memories = vec![42];
414 let globals = vec![17];
415 coreinstances.instance(module_index, memories, globals);
416
417 let mut module = Module::new();
418 module.section(&coreinstances);
419 let wasm_bytes = module.finish();
420
421 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
422 match parser.next() {
423 Some(Ok(Payload::Version { .. })) => {}
424 _ => panic!(""),
425 }
426
427 let payload = parser
428 .next()
429 .expect("parser is not empty")
430 .expect("element is a payload");
431 match payload {
432 Payload::CustomSection(section) => {
433 assert_eq!(section.name(), "coreinstances");
434 let coreinstances = match section.as_known() {
435 KnownCustom::CoreDumpInstances(s) => s,
436 _ => panic!("not coreinstances"),
437 };
438 assert_eq!(coreinstances.instances.len(), 1);
439 let instance = coreinstances
440 .instances
441 .first()
442 .expect("instance is encoded");
443 assert_eq!(instance.module_index, 0);
444 assert_eq!(instance.memories.len(), 1);
445 assert_eq!(instance.globals.len(), 1);
446 }
447 _ => panic!("unexpected payload"),
448 }
449 }
450
451 #[test]
454 fn test_roundtrip_corestack() {
455 let mut corestack = CoreDumpStackSection::new("main");
456 corestack.frame(
457 0,
458 12,
459 0,
460 vec![CoreDumpValue::I32(10)],
461 vec![CoreDumpValue::I32(42)],
462 );
463 let mut module = Module::new();
464 module.section(&corestack);
465 let wasm_bytes = module.finish();
466
467 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
468 match parser.next() {
469 Some(Ok(Payload::Version { .. })) => {}
470 _ => panic!(""),
471 }
472
473 let payload = parser
474 .next()
475 .expect("parser is not empty")
476 .expect("element is a payload");
477 match payload {
478 Payload::CustomSection(section) => {
479 assert_eq!(section.name(), "corestack");
480 let corestack = match section.as_known() {
481 KnownCustom::CoreDumpStack(s) => s,
482 _ => panic!("not a corestack section"),
483 };
484 assert_eq!(corestack.name, "main");
485 assert_eq!(corestack.frames.len(), 1);
486 let frame = corestack
487 .frames
488 .first()
489 .expect("frame is encoded in corestack");
490 assert_eq!(frame.instanceidx, 0);
491 assert_eq!(frame.funcidx, 12);
492 assert_eq!(frame.codeoffset, 0);
493 assert_eq!(frame.locals.len(), 1);
494 match frame.locals.first().expect("frame contains a local") {
495 &wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 10),
496 _ => panic!("unexpected local value"),
497 }
498 assert_eq!(frame.stack.len(), 1);
499 match frame.stack.first().expect("stack contains a value") {
500 &wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 42),
501 _ => panic!("unexpected stack value"),
502 }
503 }
504 _ => panic!("unexpected payload"),
505 }
506 }
507
508 #[test]
509 fn test_encode_coredump_section() {
510 let core = CoreDumpSection::new("test");
511
512 let mut encoded = vec![];
513 core.encode(&mut encoded);
514
515 #[rustfmt::skip]
516 assert_eq!(encoded, vec![
517 11,
519 4,
521 b'c',b'o',b'r',b'e',
523 0, 4, b't', b'e', b's', b't',
525 ]);
526 }
527
528 #[test]
529 fn test_encode_coremodules_section() {
530 let mut modules = CoreDumpModulesSection::new();
531 modules.module("mod1");
532 modules.module("mod2");
533
534 let mut encoded = vec![];
535 modules.encode(&mut encoded);
536
537 #[rustfmt::skip]
538 assert_eq!(encoded, vec![
539 25,
541 11,
543 b'c',b'o',b'r',b'e',b'm',b'o',b'd',b'u',b'l',b'e',b's',
545 2,
547 0x0, 4, b'm',b'o',b'd',b'1',
549 0x0, 4, b'm',b'o',b'd',b'2'
551 ]);
552 }
553
554 #[test]
555 fn test_encode_coreinstances_section() {
556 let mut instances = CoreDumpInstancesSection::new();
557 instances.instance(0, vec![42], vec![17]);
558
559 let mut encoded = vec![];
560 instances.encode(&mut encoded);
561
562 #[rustfmt::skip]
563 assert_eq!(encoded, vec![
564 21,
566 13,
568 b'c',b'o',b'r',b'e',b'i',b'n',b's',b't',b'a',b'n',b'c',b'e',b's',
570 1,
572 0x0, 0,
574 1, 42,
576 1, 17
578 ]);
579 }
580
581 #[test]
582 fn test_encode_corestack_section() {
583 let mut thread = CoreDumpStackSection::new("main");
584 thread.frame(
585 0,
586 42,
587 51,
588 vec![CoreDumpValue::I32(1)],
589 vec![CoreDumpValue::I32(2)],
590 );
591
592 let mut encoded = vec![];
593 thread.encode(&mut encoded);
594
595 #[rustfmt::skip]
596 assert_eq!(
597 encoded,
598 vec![
599 27,
601 9,
603 b'c',b'o',b'r',b'e',b's',b't',b'a',b'c',b'k',
605 0, 4,
607 b'm',b'a',b'i',b'n',
609 1,
611 0, 0, 42, 51,
613 1,
615 0x7F,
617 1,
619 1,
621 0x7F,
623 2
625
626 ]
627 );
628 }
629}