xref: /wasmtime-44.0.1/tests/all/debug.rs (revision bd7b59da)
1 //! Tests for instrumentation-based debugging.
2 
3 use wasmtime::{AsContextMut, Caller, Config, Engine, Extern, Func, Instance, Module, Store};
4 
5 fn get_module_and_store<C: Fn(&mut Config)>(
6     c: C,
7     wat: &str,
8 ) -> anyhow::Result<(Module, Store<()>)> {
9     let mut config = Config::default();
10     config.guest_debug(true);
11     config.wasm_exceptions(true);
12     c(&mut config);
13     let engine = Engine::new(&config)?;
14     let module = Module::new(&engine, wat)?;
15     Ok((module, Store::new(&engine, ())))
16 }
17 
18 fn test_stack_values<C: Fn(&mut Config), F: Fn(Caller<'_, ()>) + Send + Sync + 'static>(
19     wat: &str,
20     c: C,
21     f: F,
22 ) -> anyhow::Result<()> {
23     let (module, mut store) = get_module_and_store(c, wat)?;
24     let func = Func::wrap(&mut store, move |caller: Caller<'_, ()>| {
25         f(caller);
26     });
27     let instance = Instance::new(&mut store, &module, &[Extern::Func(func)])?;
28     let mut results = [];
29     instance
30         .get_func(&mut store, "main")
31         .unwrap()
32         .call(&mut store, &[], &mut results)?;
33 
34     Ok(())
35 }
36 
37 #[test]
38 #[cfg_attr(miri, ignore)]
39 fn stack_values_two_frames() -> anyhow::Result<()> {
40     let _ = env_logger::try_init();
41 
42     for inlining in [false, true] {
43         test_stack_values(
44             r#"
45     (module
46       (import "" "host" (func))
47       (func (export "main")
48         i32.const 1
49         i32.const 2
50         call 2
51         drop)
52       (func (param i32 i32) (result i32)
53         local.get 0
54         local.get 1
55         call 0
56         i32.add))
57     "#,
58             |config| {
59                 config.compiler_inlining(inlining);
60                 if inlining {
61                     unsafe {
62                         config.cranelift_flag_set("wasmtime_inlining_intra_module", "true");
63                     }
64                 }
65             },
66             |mut caller: Caller<'_, ()>| {
67                 let mut stack = caller.debug_frames().unwrap();
68                 assert!(!stack.done());
69                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 1);
70                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 65);
71 
72                 assert_eq!(stack.num_locals(), 2);
73                 assert_eq!(stack.num_stacks(), 2);
74                 assert_eq!(stack.local(0).unwrap_i32(), 1);
75                 assert_eq!(stack.local(1).unwrap_i32(), 2);
76                 assert_eq!(stack.stack(0).unwrap_i32(), 1);
77                 assert_eq!(stack.stack(1).unwrap_i32(), 2);
78 
79                 stack.move_to_parent();
80                 assert!(!stack.done());
81                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
82                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 55);
83 
84                 stack.move_to_parent();
85                 assert!(stack.done());
86             },
87         )?;
88     }
89     Ok(())
90 }
91 
92 #[test]
93 #[cfg_attr(miri, ignore)]
94 fn stack_values_exceptions() -> anyhow::Result<()> {
95     test_stack_values(
96         r#"
97     (module
98       (tag $t (param i32))
99       (import "" "host" (func))
100       (func (export "main")
101         (block $b (result i32)
102           (try_table (catch $t $b)
103             (throw $t (i32.const 42)))
104           i32.const 0)
105         (call 0)
106         (drop)))
107     "#,
108         |_config| {},
109         |mut caller: Caller<'_, ()>| {
110             let mut stack = caller.debug_frames().unwrap();
111             assert!(!stack.done());
112             assert_eq!(stack.num_stacks(), 1);
113             assert_eq!(stack.stack(0).unwrap_i32(), 42);
114             stack.move_to_parent();
115             assert!(stack.done());
116         },
117     )
118 }
119 
120 #[test]
121 #[cfg_attr(miri, ignore)]
122 fn stack_values_dead_gc_ref() -> anyhow::Result<()> {
123     test_stack_values(
124         r#"
125     (module
126       (type $s (struct))
127       (import "" "host" (func))
128       (func (export "main")
129         (struct.new $s)
130         (call 0)
131         (drop)))
132     "#,
133         |config| {
134             config.wasm_gc(true);
135         },
136         |mut caller: Caller<'_, ()>| {
137             let mut stack = caller.debug_frames().unwrap();
138             assert!(!stack.done());
139             assert_eq!(stack.num_stacks(), 1);
140             assert!(stack.stack(0).unwrap_anyref().is_some());
141             stack.move_to_parent();
142             assert!(stack.done());
143         },
144     )
145 }
146 
147 #[test]
148 #[cfg_attr(miri, ignore)]
149 fn gc_access_during_call() -> anyhow::Result<()> {
150     test_stack_values(
151         r#"
152     (module
153       (type $s (struct (field i32)))
154       (import "" "host" (func))
155       (func (export "main")
156         (local $l (ref null $s))
157         (local.set $l (struct.new $s (i32.const 42)))
158         (call 0)))
159     "#,
160         |config| {
161             config.wasm_gc(true);
162         },
163         |mut caller: Caller<'_, ()>| {
164             let mut stack = caller.debug_frames().unwrap();
165 
166             // Do a GC while we hold the stack cursor.
167             stack.as_context_mut().gc(None);
168 
169             assert!(!stack.done());
170             assert_eq!(stack.num_stacks(), 0);
171             assert_eq!(stack.num_locals(), 1);
172             // Note that this struct is dead during the call, and the
173             // ref could otherwise be optimized away (no longer in the
174             // stackmap at this point); but we verify it is still
175             // alive here because it is rooted in the
176             // debug-instrumentation slot.
177             let s = stack
178                 .local(0)
179                 .unwrap_any_ref()
180                 .unwrap()
181                 .unwrap_struct(&stack)
182                 .unwrap();
183             assert_eq!(s.field(&mut stack, 0).unwrap().unwrap_i32(), 42);
184             stack.move_to_parent();
185             assert!(stack.done());
186         },
187     )
188 }
189