wasm_encoder/core/
dump.rs

1use crate::{CustomSection, Encode, Section};
2use alloc::borrow::Cow;
3use alloc::string::String;
4use alloc::vec;
5use alloc::vec::Vec;
6
7/// The "core" custom section for coredumps, as described in the
8/// [tool-conventions
9/// repository](https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md).
10///
11/// There are four sections that comprise a core dump:
12///     - "core", which contains the name of the core dump
13///     - "coremodules", a listing of modules
14///     - "coreinstances", a listing of module instances
15///     - "corestack", a listing of frames for a specific thread
16///
17/// # Example of how these could be constructed and encoded into a module:
18///
19/// ```
20/// use wasm_encoder::{
21///     CoreDumpInstancesSection, CoreDumpModulesSection, CoreDumpSection, CoreDumpStackSection,
22///     CoreDumpValue, Module,
23/// };
24/// let core = CoreDumpSection::new("MyModule.wasm");
25///
26/// let mut modules = CoreDumpModulesSection::new();
27/// modules.module("my_module");
28///
29/// let mut instances = CoreDumpInstancesSection::new();
30/// let module_idx = 0;
31/// let memories = vec![1];
32/// let globals = vec![2];
33/// instances.instance(module_idx, memories, globals);
34///
35/// let mut thread = CoreDumpStackSection::new("main");
36/// let instance_index = 0;
37/// let func_index = 42;
38/// let code_offset = 0x1234;
39/// let locals = vec![CoreDumpValue::I32(1)];
40/// let stack = vec![CoreDumpValue::I32(2)];
41/// thread.frame(instance_index, func_index, code_offset, locals, stack);
42///
43/// let mut module = Module::new();
44/// module.section(&core);
45/// module.section(&modules);
46/// module.section(&instances);
47/// module.section(&thread);
48/// ```
49#[derive(Clone, Debug, Default)]
50pub struct CoreDumpSection {
51    name: String,
52}
53
54impl CoreDumpSection {
55    /// Create a new core dump section encoder
56    pub fn new(name: impl Into<String>) -> Self {
57        let name = name.into();
58        CoreDumpSection { name }
59    }
60
61    /// View the encoded section as a CustomSection.
62    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/// The "coremodules" custom section for coredumps which lists the names of the
85/// modules
86///
87/// # Example
88///
89/// ```
90/// use wasm_encoder::{CoreDumpModulesSection, Module};
91/// let mut modules_section = CoreDumpModulesSection::new();
92/// modules_section.module("my_module");
93/// let mut module = Module::new();
94/// module.section(&modules_section);
95/// ```
96#[derive(Debug)]
97pub struct CoreDumpModulesSection {
98    num_added: u32,
99    bytes: Vec<u8>,
100}
101
102impl CoreDumpModulesSection {
103    /// Create a new core dump modules section encoder.
104    pub fn new() -> Self {
105        CoreDumpModulesSection {
106            bytes: vec![],
107            num_added: 0,
108        }
109    }
110
111    /// View the encoded section as a CustomSection.
112    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    /// Encode a module name into the section's bytes.
123    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    /// The number of modules that are encoded in the section.
131    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/// The "coreinstances" section for the core dump
149#[derive(Debug)]
150pub struct CoreDumpInstancesSection {
151    num_added: u32,
152    bytes: Vec<u8>,
153}
154
155impl CoreDumpInstancesSection {
156    /// Create a new core dump instances section encoder.
157    pub fn new() -> Self {
158        CoreDumpInstancesSection {
159            bytes: vec![],
160            num_added: 0,
161        }
162    }
163
164    /// View the encoded section as a CustomSection.
165    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    /// Encode an instance into the section's bytes.
176    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    /// The number of modules that are encoded in the section.
192    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/// A "corestack" custom section as described in the [tool-conventions
210/// repository](https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md)
211///
212/// # Example
213///
214/// ```
215/// use wasm_encoder::{CoreDumpStackSection, Module, CoreDumpValue};
216/// let mut thread = CoreDumpStackSection::new("main");
217///
218/// let instance_index = 0;
219/// let func_index = 42;
220/// let code_offset = 0x1234;
221/// let locals = vec![CoreDumpValue::I32(1)];
222/// let stack = vec![CoreDumpValue::I32(2)];
223/// thread.frame(instance_index, func_index, code_offset, locals, stack);
224///
225/// let mut module = Module::new();
226/// module.section(&thread);
227/// ```
228#[derive(Clone, Debug, Default)]
229pub struct CoreDumpStackSection {
230    frame_bytes: Vec<u8>,
231    count: u32,
232    name: String,
233}
234
235impl CoreDumpStackSection {
236    /// Create a new core dump stack section encoder.
237    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    /// Add a stack frame to this coredump stack section.
247    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    /// View the encoded section as a CustomSection.
272    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/// Local and stack values are encoded using one byte for the type (similar to
297/// Wasm's Number Types) followed by bytes representing the actual value
298/// See the tool-conventions repo for more details.
299#[derive(Clone, Debug)]
300pub enum CoreDumpValue {
301    /// a missing value (usually missing because it was optimized out)
302    Missing,
303    /// An i32 value
304    I32(i32),
305    /// An i64 value
306    I64(i64),
307    /// An f32 value
308    F32(f32),
309    /// An f64 value
310    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    // Create new core dump section and test whether it is properly encoded and
344    // parsed back out by wasmparser
345    #[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    // Create new corestack section and test whether it is properly encoded and
452    // parsed back out by wasmparser
453    #[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            // section length
518            11,
519            // name length
520            4,
521            // section name (core)
522            b'c',b'o',b'r',b'e',
523            // process-info (0, data length, data)
524            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            // section length
540            25,
541            // name length
542            11,
543            // section name (coremodules)
544            b'c',b'o',b'r',b'e',b'm',b'o',b'd',b'u',b'l',b'e',b's',
545            // module count
546            2,
547            // 0x0, name-length, module name (mod1)
548            0x0, 4, b'm',b'o',b'd',b'1',
549            // 0x0, name-length, module name (mod2)
550            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            // section length
565            21,
566            // name length
567            13,
568            // section name (coreinstances)
569            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            // instance count
571            1,
572            // 0x0, module_idx
573            0x0, 0,
574            // memories count, memories
575            1, 42,
576            // globals count, globals
577            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                // section length
600                27,
601                // length of name.
602                9,
603                // section name (corestack)
604                b'c',b'o',b'r',b'e',b's',b't',b'a',b'c',b'k',
605                // 0x0, thread name length
606                0, 4,
607                // thread name (main)
608                b'm',b'a',b'i',b'n',
609                // frame count
610                1,
611                // 0x0, instanceidx, funcidx, codeoffset
612                0, 0, 42, 51,
613                // local count
614                1,
615                // local value type
616                0x7F,
617                // local value
618                1,
619                // stack count
620                1,
621                // stack value type
622                0x7F,
623                // stack value
624                2
625
626            ]
627        );
628    }
629}