1use std::borrow::Cow;
2
3use crate::{CustomSection, Encode, Section};
4
5/// The "core" custom section for coredumps, as described in the
6/// [tool-conventions
7/// repository](https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md).
8///
9/// There are four sections that comprise a core dump:
10/// - "core", which contains the name of the core dump
11/// - "coremodules", a listing of modules
12/// - "coreinstances", a listing of module instances
13/// - "corestack", a listing of frames for a specific thread
14///
15/// # Example of how these could be constructed and encoded into a module:
16///
17/// ```
18/// use wasm_encoder::{
19/// CoreDumpInstancesSection, CoreDumpModulesSection, CoreDumpSection, CoreDumpStackSection,
20/// CoreDumpValue, Module,
21/// };
22/// let core = CoreDumpSection::new("MyModule.wasm");
23///
24/// let mut modules = CoreDumpModulesSection::new();
25/// modules.module("my_module");
26///
27/// let mut instances = CoreDumpInstancesSection::new();
28/// let module_idx = 0;
29/// let memories = vec![1];
30/// let globals = vec![2];
31/// instances.instance(module_idx, memories, globals);
32///
33/// let mut thread = CoreDumpStackSection::new("main");
34/// let instance_index = 0;
35/// let func_index = 42;
36/// let code_offset = 0x1234;
37/// let locals = vec![CoreDumpValue::I32(1)];
38/// let stack = vec![CoreDumpValue::I32(2)];
39/// thread.frame(instance_index, func_index, code_offset, locals, stack);
40///
41/// let mut module = Module::new();
42/// module.section(&core);
43/// module.section(&modules);
44/// module.section(&instances);
45/// module.section(&thread);
46/// ```
47#[derive(Clone, Debug, Default)]
48pub struct CoreDumpSection {
49 name: String,
50}
51
52impl CoreDumpSection {
53 /// Create a new core dump section encoder
54 pub fn new(name: impl Into<String>) -> Self {
55 let name: String = name.into();
56 CoreDumpSection { name }
57 }
58
59 /// View the encoded section as a CustomSection.
60 fn as_custom<'a>(&'a self) -> CustomSection<'a> {
61 let mut data: Vec = vec![0];
62 self.name.encode(&mut data);
63 CustomSection {
64 name: "core".into(),
65 data: Cow::Owned(data),
66 }
67 }
68}
69
70impl Encode for CoreDumpSection {
71 fn encode(&self, sink: &mut Vec<u8>) {
72 self.as_custom().encode(sink);
73 }
74}
75
76impl Section for CoreDumpSection {
77 fn id(&self) -> u8 {
78 crate::core::SectionId::Custom as u8
79 }
80}
81
82/// The "coremodules" custom section for coredumps which lists the names of the
83/// modules
84///
85/// # Example
86///
87/// ```
88/// use wasm_encoder::{CoreDumpModulesSection, Module};
89/// let mut modules_section = CoreDumpModulesSection::new();
90/// modules_section.module("my_module");
91/// let mut module = Module::new();
92/// module.section(&modules_section);
93/// ```
94#[derive(Debug)]
95pub struct CoreDumpModulesSection {
96 num_added: u32,
97 bytes: Vec<u8>,
98}
99
100impl CoreDumpModulesSection {
101 /// Create a new core dump modules section encoder.
102 pub fn new() -> Self {
103 CoreDumpModulesSection {
104 bytes: vec![],
105 num_added: 0,
106 }
107 }
108
109 /// View the encoded section as a CustomSection.
110 pub fn as_custom(&self) -> CustomSection<'_> {
111 let mut data = vec![];
112 self.num_added.encode(&mut data);
113 data.extend(self.bytes.iter().copied());
114 CustomSection {
115 name: "coremodules".into(),
116 data: Cow::Owned(data),
117 }
118 }
119
120 /// Encode a module name into the section's bytes.
121 pub fn module(&mut self, module_name: impl AsRef<str>) -> &mut Self {
122 self.bytes.push(0x0);
123 module_name.as_ref().encode(&mut self.bytes);
124 self.num_added += 1;
125 self
126 }
127
128 /// The number of modules that are encoded in the section.
129 pub fn len(&self) -> u32 {
130 self.num_added
131 }
132}
133
134impl Encode for CoreDumpModulesSection {
135 fn encode(&self, sink: &mut Vec<u8>) {
136 self.as_custom().encode(sink);
137 }
138}
139
140impl Section for CoreDumpModulesSection {
141 fn id(&self) -> u8 {
142 crate::core::SectionId::Custom as u8
143 }
144}
145
146/// The "coreinstances" section for the core dump
147#[derive(Debug)]
148pub struct CoreDumpInstancesSection {
149 num_added: u32,
150 bytes: Vec<u8>,
151}
152
153impl CoreDumpInstancesSection {
154 /// Create a new core dump instances section encoder.
155 pub fn new() -> Self {
156 CoreDumpInstancesSection {
157 bytes: vec![],
158 num_added: 0,
159 }
160 }
161
162 /// View the encoded section as a CustomSection.
163 pub fn as_custom(&self) -> CustomSection<'_> {
164 let mut data = vec![];
165 self.num_added.encode(&mut data);
166 data.extend(self.bytes.iter().copied());
167 CustomSection {
168 name: "coreinstances".into(),
169 data: Cow::Owned(data),
170 }
171 }
172
173 /// Encode an instance into the section's bytes.
174 pub fn instance<M, G>(&mut self, module_index: u32, memories: M, globals: G) -> &mut Self
175 where
176 M: IntoIterator<Item = u32>,
177 <M as IntoIterator>::IntoIter: ExactSizeIterator,
178 G: IntoIterator<Item = u32>,
179 <G as IntoIterator>::IntoIter: ExactSizeIterator,
180 {
181 self.bytes.push(0x0);
182 module_index.encode(&mut self.bytes);
183 crate::encode_vec(memories, &mut self.bytes);
184 crate::encode_vec(globals, &mut self.bytes);
185 self.num_added += 1;
186 self
187 }
188
189 /// The number of modules that are encoded in the section.
190 pub fn len(&self) -> u32 {
191 self.num_added
192 }
193}
194
195impl Encode for CoreDumpInstancesSection {
196 fn encode(&self, sink: &mut Vec<u8>) {
197 self.as_custom().encode(sink);
198 }
199}
200
201impl Section for CoreDumpInstancesSection {
202 fn id(&self) -> u8 {
203 crate::core::SectionId::Custom as u8
204 }
205}
206
207/// A "corestack" custom section as described in the [tool-conventions
208/// repository](https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md)
209///
210/// # Example
211///
212/// ```
213/// use wasm_encoder::{CoreDumpStackSection, Module, CoreDumpValue};
214/// let mut thread = CoreDumpStackSection::new("main");
215///
216/// let instance_index = 0;
217/// let func_index = 42;
218/// let code_offset = 0x1234;
219/// let locals = vec![CoreDumpValue::I32(1)];
220/// let stack = vec![CoreDumpValue::I32(2)];
221/// thread.frame(instance_index, func_index, code_offset, locals, stack);
222///
223/// let mut module = Module::new();
224/// module.section(&thread);
225/// ```
226#[derive(Clone, Debug, Default)]
227pub struct CoreDumpStackSection {
228 frame_bytes: Vec<u8>,
229 count: u32,
230 name: String,
231}
232
233impl CoreDumpStackSection {
234 /// Create a new core dump stack section encoder.
235 pub fn new(name: impl Into<String>) -> Self {
236 let name = name.into();
237 CoreDumpStackSection {
238 frame_bytes: Vec::new(),
239 count: 0,
240 name,
241 }
242 }
243
244 /// Add a stack frame to this coredump stack section.
245 pub fn frame<L, S>(
246 &mut self,
247 instanceidx: u32,
248 funcidx: u32,
249 codeoffset: u32,
250 locals: L,
251 stack: S,
252 ) -> &mut Self
253 where
254 L: IntoIterator<Item = CoreDumpValue>,
255 <L as IntoIterator>::IntoIter: ExactSizeIterator,
256 S: IntoIterator<Item = CoreDumpValue>,
257 <S as IntoIterator>::IntoIter: ExactSizeIterator,
258 {
259 self.count += 1;
260 self.frame_bytes.push(0);
261 instanceidx.encode(&mut self.frame_bytes);
262 funcidx.encode(&mut self.frame_bytes);
263 codeoffset.encode(&mut self.frame_bytes);
264 crate::encode_vec(locals, &mut self.frame_bytes);
265 crate::encode_vec(stack, &mut self.frame_bytes);
266 self
267 }
268
269 /// View the encoded section as a CustomSection.
270 pub fn as_custom<'a>(&'a self) -> CustomSection<'a> {
271 let mut data = vec![0];
272 self.name.encode(&mut data);
273 self.count.encode(&mut data);
274 data.extend(&self.frame_bytes);
275 CustomSection {
276 name: "corestack".into(),
277 data: Cow::Owned(data),
278 }
279 }
280}
281
282impl Encode for CoreDumpStackSection {
283 fn encode(&self, sink: &mut Vec<u8>) {
284 self.as_custom().encode(sink);
285 }
286}
287
288impl Section for CoreDumpStackSection {
289 fn id(&self) -> u8 {
290 crate::core::SectionId::Custom as u8
291 }
292}
293
294/// Local and stack values are encoded using one byte for the type (similar to
295/// Wasm's Number Types) followed by bytes representing the actual value
296/// See the tool-conventions repo for more details.
297#[derive(Clone, Debug)]
298pub enum CoreDumpValue {
299 /// a missing value (usually missing because it was optimized out)
300 Missing,
301 /// An i32 value
302 I32(i32),
303 /// An i64 value
304 I64(i64),
305 /// An f32 value
306 F32(f32),
307 /// An f64 value
308 F64(f64),
309}
310
311impl Encode for CoreDumpValue {
312 fn encode(&self, sink: &mut Vec<u8>) {
313 match self {
314 CoreDumpValue::Missing => sink.push(0x01),
315 CoreDumpValue::I32(x: &i32) => {
316 sink.push(0x7F);
317 x.encode(sink);
318 }
319 CoreDumpValue::I64(x: &i64) => {
320 sink.push(0x7E);
321 x.encode(sink);
322 }
323 CoreDumpValue::F32(x: &f32) => {
324 sink.push(0x7D);
325 x.encode(sink);
326 }
327 CoreDumpValue::F64(x: &f64) => {
328 sink.push(0x7C);
329 x.encode(sink);
330 }
331 }
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use crate::Module;
339 use wasmparser::{KnownCustom, Parser, Payload};
340
341 // Create new core dump section and test whether it is properly encoded and
342 // parsed back out by wasmparser
343 #[test]
344 fn test_roundtrip_core() {
345 let core = CoreDumpSection::new("test.wasm");
346 let mut module = Module::new();
347 module.section(&core);
348
349 let wasm_bytes = module.finish();
350
351 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
352 match parser.next() {
353 Some(Ok(Payload::Version { .. })) => {}
354 _ => panic!(""),
355 }
356
357 let payload = parser
358 .next()
359 .expect("parser is not empty")
360 .expect("element is a payload");
361 match payload {
362 Payload::CustomSection(section) => {
363 assert_eq!(section.name(), "core");
364 let core = match section.as_known() {
365 KnownCustom::CoreDump(s) => s,
366 _ => panic!("not coredump"),
367 };
368 assert_eq!(core.name, "test.wasm");
369 }
370 _ => panic!("unexpected payload"),
371 }
372 }
373
374 #[test]
375 fn test_roundtrip_coremodules() {
376 let mut coremodules = CoreDumpModulesSection::new();
377 coremodules.module("test_module");
378
379 let mut module = crate::Module::new();
380 module.section(&coremodules);
381
382 let wasm_bytes = module.finish();
383
384 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
385 match parser.next() {
386 Some(Ok(Payload::Version { .. })) => {}
387 _ => panic!(""),
388 }
389
390 let payload = parser
391 .next()
392 .expect("parser is not empty")
393 .expect("element is a payload");
394 match payload {
395 Payload::CustomSection(section) => {
396 assert_eq!(section.name(), "coremodules");
397 let modules = match section.as_known() {
398 KnownCustom::CoreDumpModules(s) => s,
399 _ => panic!("not coremodules"),
400 };
401 assert_eq!(modules.modules[0], "test_module");
402 }
403 _ => panic!("unexpected payload"),
404 }
405 }
406
407 #[test]
408 fn test_roundtrip_coreinstances() {
409 let mut coreinstances = CoreDumpInstancesSection::new();
410 let module_index = 0;
411 let memories = vec![42];
412 let globals = vec![17];
413 coreinstances.instance(module_index, memories, globals);
414
415 let mut module = Module::new();
416 module.section(&coreinstances);
417 let wasm_bytes = module.finish();
418
419 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
420 match parser.next() {
421 Some(Ok(Payload::Version { .. })) => {}
422 _ => panic!(""),
423 }
424
425 let payload = parser
426 .next()
427 .expect("parser is not empty")
428 .expect("element is a payload");
429 match payload {
430 Payload::CustomSection(section) => {
431 assert_eq!(section.name(), "coreinstances");
432 let coreinstances = match section.as_known() {
433 KnownCustom::CoreDumpInstances(s) => s,
434 _ => panic!("not coreinstances"),
435 };
436 assert_eq!(coreinstances.instances.len(), 1);
437 let instance = coreinstances
438 .instances
439 .first()
440 .expect("instance is encoded");
441 assert_eq!(instance.module_index, 0);
442 assert_eq!(instance.memories.len(), 1);
443 assert_eq!(instance.globals.len(), 1);
444 }
445 _ => panic!("unexpected payload"),
446 }
447 }
448
449 // Create new corestack section and test whether it is properly encoded and
450 // parsed back out by wasmparser
451 #[test]
452 fn test_roundtrip_corestack() {
453 let mut corestack = CoreDumpStackSection::new("main");
454 corestack.frame(
455 0,
456 12,
457 0,
458 vec![CoreDumpValue::I32(10)],
459 vec![CoreDumpValue::I32(42)],
460 );
461 let mut module = Module::new();
462 module.section(&corestack);
463 let wasm_bytes = module.finish();
464
465 let mut parser = Parser::new(0).parse_all(&wasm_bytes);
466 match parser.next() {
467 Some(Ok(Payload::Version { .. })) => {}
468 _ => panic!(""),
469 }
470
471 let payload = parser
472 .next()
473 .expect("parser is not empty")
474 .expect("element is a payload");
475 match payload {
476 Payload::CustomSection(section) => {
477 assert_eq!(section.name(), "corestack");
478 let corestack = match section.as_known() {
479 KnownCustom::CoreDumpStack(s) => s,
480 _ => panic!("not a corestack section"),
481 };
482 assert_eq!(corestack.name, "main");
483 assert_eq!(corestack.frames.len(), 1);
484 let frame = corestack
485 .frames
486 .first()
487 .expect("frame is encoded in corestack");
488 assert_eq!(frame.instanceidx, 0);
489 assert_eq!(frame.funcidx, 12);
490 assert_eq!(frame.codeoffset, 0);
491 assert_eq!(frame.locals.len(), 1);
492 match frame.locals.first().expect("frame contains a local") {
493 &wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 10),
494 _ => panic!("unexpected local value"),
495 }
496 assert_eq!(frame.stack.len(), 1);
497 match frame.stack.first().expect("stack contains a value") {
498 &wasmparser::CoreDumpValue::I32(val) => assert_eq!(val, 42),
499 _ => panic!("unexpected stack value"),
500 }
501 }
502 _ => panic!("unexpected payload"),
503 }
504 }
505
506 #[test]
507 fn test_encode_coredump_section() {
508 let core = CoreDumpSection::new("test");
509
510 let mut encoded = vec![];
511 core.encode(&mut encoded);
512
513 #[rustfmt::skip]
514 assert_eq!(encoded, vec![
515 // section length
516 11,
517 // name length
518 4,
519 // section name (core)
520 b'c',b'o',b'r',b'e',
521 // process-info (0, data length, data)
522 0, 4, b't', b'e', b's', b't',
523 ]);
524 }
525
526 #[test]
527 fn test_encode_coremodules_section() {
528 let mut modules = CoreDumpModulesSection::new();
529 modules.module("mod1");
530 modules.module("mod2");
531
532 let mut encoded = vec![];
533 modules.encode(&mut encoded);
534
535 #[rustfmt::skip]
536 assert_eq!(encoded, vec![
537 // section length
538 25,
539 // name length
540 11,
541 // section name (coremodules)
542 b'c',b'o',b'r',b'e',b'm',b'o',b'd',b'u',b'l',b'e',b's',
543 // module count
544 2,
545 // 0x0, name-length, module name (mod1)
546 0x0, 4, b'm',b'o',b'd',b'1',
547 // 0x0, name-length, module name (mod2)
548 0x0, 4, b'm',b'o',b'd',b'2'
549 ]);
550 }
551
552 #[test]
553 fn test_encode_coreinstances_section() {
554 let mut instances = CoreDumpInstancesSection::new();
555 instances.instance(0, vec![42], vec![17]);
556
557 let mut encoded = vec![];
558 instances.encode(&mut encoded);
559
560 #[rustfmt::skip]
561 assert_eq!(encoded, vec![
562 // section length
563 21,
564 // name length
565 13,
566 // section name (coreinstances)
567 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',
568 // instance count
569 1,
570 // 0x0, module_idx
571 0x0, 0,
572 // memories count, memories
573 1, 42,
574 // globals count, globals
575 1, 17
576 ]);
577 }
578
579 #[test]
580 fn test_encode_corestack_section() {
581 let mut thread = CoreDumpStackSection::new("main");
582 thread.frame(
583 0,
584 42,
585 51,
586 vec![CoreDumpValue::I32(1)],
587 vec![CoreDumpValue::I32(2)],
588 );
589
590 let mut encoded = vec![];
591 thread.encode(&mut encoded);
592
593 #[rustfmt::skip]
594 assert_eq!(
595 encoded,
596 vec![
597 // section length
598 27,
599 // length of name.
600 9,
601 // section name (corestack)
602 b'c',b'o',b'r',b'e',b's',b't',b'a',b'c',b'k',
603 // 0x0, thread name length
604 0, 4,
605 // thread name (main)
606 b'm',b'a',b'i',b'n',
607 // frame count
608 1,
609 // 0x0, instanceidx, funcidx, codeoffset
610 0, 0, 42, 51,
611 // local count
612 1,
613 // local value type
614 0x7F,
615 // local value
616 1,
617 // stack count
618 1,
619 // stack value type
620 0x7F,
621 // stack value
622 2
623
624 ]
625 );
626 }
627}
628