xref: /wasmtime-44.0.1/tests/all/debug.rs (revision e4e9cabe)
1 //! Tests for instrumentation-based debugging.
2 
3 use std::sync::Arc;
4 use std::sync::atomic::{AtomicUsize, Ordering};
5 use wasmtime::{
6     AsContextMut, Caller, Config, DebugEvent, DebugHandler, Engine, Extern, FrameParentResult,
7     Func, Global, GlobalType, Instance, Module, Mutability, Store, StoreContextMut, Val, ValType,
8 };
9 
10 #[test]
11 fn debugging_does_not_work_with_signal_based_traps() {
12     let mut config = Config::default();
13     config.guest_debug(true).signals_based_traps(true);
14     let err = Engine::new(&config).expect_err("invalid config should produce an error");
15     assert!(format!("{err:?}").contains("cannot use signals-based traps"));
16 }
17 
18 #[test]
19 fn debugging_apis_are_denied_without_debugging() -> wasmtime::Result<()> {
20     let mut config = Config::default();
21     config.guest_debug(false);
22     let engine = Engine::new(&config)?;
23     let module = Module::new(&engine, "(module (global $g (mut i32) (i32.const 0)))")?;
24     let mut store = Store::new(&engine, ());
25     let instance = Instance::new(&mut store, &module, &[])?;
26 
27     assert!(store.debug_frames().is_none());
28     assert!(instance.debug_global(&mut store, 0).is_none());
29 
30     Ok(())
31 }
32 
33 fn get_module_and_store<C: Fn(&mut Config)>(
34     c: C,
35     wat: &str,
36 ) -> wasmtime::Result<(Module, Store<()>)> {
37     let mut config = Config::default();
38     config.guest_debug(true);
39     config.wasm_exceptions(true);
40     c(&mut config);
41     let engine = Engine::new(&config)?;
42     let module = Module::new(&engine, wat)?;
43     Ok((module, Store::new(&engine, ())))
44 }
45 
46 fn test_stack_values<C: Fn(&mut Config), F: Fn(Caller<'_, ()>) + Send + Sync + 'static>(
47     wat: &str,
48     c: C,
49     f: F,
50 ) -> wasmtime::Result<()> {
51     let (module, mut store) = get_module_and_store(c, wat)?;
52     let func = Func::wrap(&mut store, move |caller: Caller<'_, ()>| {
53         f(caller);
54     });
55     let instance = Instance::new(&mut store, &module, &[Extern::Func(func)])?;
56     let mut results = [];
57     instance
58         .get_func(&mut store, "main")
59         .unwrap()
60         .call(&mut store, &[], &mut results)?;
61 
62     Ok(())
63 }
64 
65 #[test]
66 #[cfg_attr(miri, ignore)]
67 fn stack_values_two_frames() -> wasmtime::Result<()> {
68     let _ = env_logger::try_init();
69 
70     for inlining in [false, true] {
71         test_stack_values(
72             r#"
73     (module
74       (import "" "host" (func))
75       (func (export "main")
76         i32.const 1
77         i32.const 2
78         call 2
79         drop)
80       (func (param i32 i32) (result i32)
81         local.get 0
82         local.get 1
83         call 0
84         i32.add))
85     "#,
86             |config| {
87                 config.compiler_inlining(inlining);
88                 if inlining {
89                     unsafe {
90                         config.cranelift_flag_set("wasmtime_inlining_intra_module", "true");
91                     }
92                 }
93             },
94             |mut caller: Caller<'_, ()>| {
95                 let mut stack = caller.debug_frames().unwrap();
96                 assert!(!stack.done());
97                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 1);
98                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 65);
99 
100                 assert_eq!(stack.num_locals(), 2);
101                 assert_eq!(stack.num_stacks(), 2);
102                 assert_eq!(stack.local(0).unwrap_i32(), 1);
103                 assert_eq!(stack.local(1).unwrap_i32(), 2);
104                 assert_eq!(stack.stack(0).unwrap_i32(), 1);
105                 assert_eq!(stack.stack(1).unwrap_i32(), 2);
106 
107                 assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
108                 assert!(!stack.done());
109                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
110                 assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 55);
111 
112                 assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
113                 assert!(stack.done());
114             },
115         )?;
116     }
117     Ok(())
118 }
119 
120 #[test]
121 #[cfg_attr(miri, ignore)]
122 fn stack_values_exceptions() -> wasmtime::Result<()> {
123     test_stack_values(
124         r#"
125     (module
126       (tag $t (param i32))
127       (import "" "host" (func))
128       (func (export "main")
129         (block $b (result i32)
130           (try_table (catch $t $b)
131             (throw $t (i32.const 42)))
132           i32.const 0)
133         (call 0)
134         (drop)))
135     "#,
136         |_config| {},
137         |mut caller: Caller<'_, ()>| {
138             let mut stack = caller.debug_frames().unwrap();
139             assert!(!stack.done());
140             assert_eq!(stack.num_stacks(), 1);
141             assert_eq!(stack.stack(0).unwrap_i32(), 42);
142             assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
143             assert!(stack.done());
144         },
145     )
146 }
147 
148 #[test]
149 #[cfg_attr(miri, ignore)]
150 fn stack_values_dead_gc_ref() -> wasmtime::Result<()> {
151     test_stack_values(
152         r#"
153     (module
154       (type $s (struct))
155       (import "" "host" (func))
156       (func (export "main")
157         (struct.new $s)
158         (call 0)
159         (drop)))
160     "#,
161         |config| {
162             config.wasm_gc(true);
163         },
164         |mut caller: Caller<'_, ()>| {
165             let mut stack = caller.debug_frames().unwrap();
166             assert!(!stack.done());
167             assert_eq!(stack.num_stacks(), 1);
168             assert!(stack.stack(0).unwrap_anyref().is_some());
169             assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
170             assert!(stack.done());
171         },
172     )
173 }
174 
175 #[test]
176 #[cfg_attr(miri, ignore)]
177 fn gc_access_during_call() -> wasmtime::Result<()> {
178     test_stack_values(
179         r#"
180     (module
181       (type $s (struct (field i32)))
182       (import "" "host" (func))
183       (func (export "main")
184         (local $l (ref null $s))
185         (local.set $l (struct.new $s (i32.const 42)))
186         (call 0)))
187     "#,
188         |config| {
189             config.wasm_gc(true);
190         },
191         |mut caller: Caller<'_, ()>| {
192             let mut stack = caller.debug_frames().unwrap();
193 
194             // Do a GC while we hold the stack cursor.
195             stack.as_context_mut().gc(None);
196 
197             assert!(!stack.done());
198             assert_eq!(stack.num_stacks(), 0);
199             assert_eq!(stack.num_locals(), 1);
200             // Note that this struct is dead during the call, and the
201             // ref could otherwise be optimized away (no longer in the
202             // stackmap at this point); but we verify it is still
203             // alive here because it is rooted in the
204             // debug-instrumentation slot.
205             let s = stack
206                 .local(0)
207                 .unwrap_any_ref()
208                 .unwrap()
209                 .unwrap_struct(&stack)
210                 .unwrap();
211             assert_eq!(s.field(&mut stack, 0).unwrap().unwrap_i32(), 42);
212             assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
213             assert!(stack.done());
214         },
215     )
216 }
217 
218 #[test]
219 #[cfg_attr(miri, ignore)]
220 fn stack_values_two_activations() -> wasmtime::Result<()> {
221     let _ = env_logger::try_init();
222 
223     let mut config = Config::default();
224     config.guest_debug(true);
225     config.wasm_exceptions(true);
226     let engine = Engine::new(&config)?;
227     let module1 = Module::new(
228         &engine,
229         r#"
230     (module
231       (import "" "host1" (func (param i32 i32) (result i32)))
232       (func (export "main") (result i32)
233         i32.const 1
234         i32.const 2
235         call 0))
236     "#,
237     )?;
238     let module2 = Module::new(
239         &engine,
240         r#"
241     (module
242       (import "" "host2" (func))
243       (func (export "inner") (param i32 i32) (result i32)
244         local.get 0
245         local.get 1
246         call 0
247         i32.add))
248     "#,
249     )?;
250     let mut store = Store::new(&engine, ());
251 
252     let module1_clone = module1.clone();
253     let module2_clone = module2.clone();
254     let host2 = Func::wrap(&mut store, move |mut caller: Caller<'_, ()>| {
255         let mut stack = caller.debug_frames().unwrap();
256         assert!(!stack.done());
257         assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
258         assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 56);
259         assert!(Module::same(stack.module().unwrap(), &module2_clone));
260         assert_eq!(stack.num_locals(), 2);
261         assert_eq!(stack.num_stacks(), 2);
262         assert_eq!(stack.local(0).unwrap_i32(), 1);
263         assert_eq!(stack.local(1).unwrap_i32(), 2);
264         assert_eq!(stack.stack(0).unwrap_i32(), 1);
265         assert_eq!(stack.stack(1).unwrap_i32(), 2);
266         let inner_instance = stack.instance();
267 
268         assert_eq!(stack.move_to_parent(), FrameParentResult::NewActivation);
269         assert!(!stack.done());
270 
271         assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
272         assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 56);
273         assert!(Module::same(stack.module().unwrap(), &module1_clone));
274         assert_eq!(stack.num_locals(), 0);
275         assert_eq!(stack.num_stacks(), 2);
276         assert_eq!(stack.stack(0).unwrap_i32(), 1);
277         assert_eq!(stack.stack(1).unwrap_i32(), 2);
278         let outer_instance = stack.instance();
279 
280         assert_ne!(inner_instance, outer_instance);
281 
282         assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
283         assert!(stack.done());
284     });
285 
286     let instance2 = Instance::new(&mut store, &module2, &[Extern::Func(host2)])?;
287     let inner = instance2.get_func(&mut store, "inner").unwrap();
288 
289     let host1 = Func::wrap(
290         &mut store,
291         move |mut caller: Caller<'_, ()>, a: i32, b: i32| -> i32 {
292             let mut results = [Val::I32(0)];
293             inner
294                 .call(&mut caller, &[Val::I32(a), Val::I32(b)], &mut results[..])
295                 .unwrap();
296             results[0].unwrap_i32()
297         },
298     );
299 
300     let instance1 = Instance::new(&mut store, &module1, &[Extern::Func(host1)])?;
301     let main = instance1.get_func(&mut store, "main").unwrap();
302 
303     let mut results = [Val::I32(0)];
304     main.call(&mut store, &[], &mut results)?;
305     assert_eq!(results[0].unwrap_i32(), 3);
306     Ok(())
307 }
308 
309 #[test]
310 #[cfg_attr(miri, ignore)]
311 fn debug_frames_on_store_with_no_wasm_activation() -> wasmtime::Result<()> {
312     let mut config = Config::default();
313     config.guest_debug(true);
314     let engine = Engine::new(&config)?;
315     let mut store = Store::new(&engine, ());
316     let frames = store
317         .debug_frames()
318         .expect("Debug frames should be available");
319     assert!(frames.done());
320     Ok(())
321 }
322 
323 #[test]
324 #[cfg_attr(miri, ignore)]
325 fn private_entity_access() -> wasmtime::Result<()> {
326     let mut config = Config::default();
327     config.guest_debug(true);
328     config.wasm_gc(true);
329     config.gc_support(true);
330     config.wasm_exceptions(true);
331     let engine = Engine::new(&config)?;
332     let mut store = Store::new(&engine, ());
333     let module = Module::new(
334         &engine,
335         r#"
336         (module
337           (import "" "i" (global (mut i32)))
338           (import "" "f" (func (result i32)))
339           (global $g (mut i32) (i32.const 0))
340           (memory $m 1 1)
341           (table $t 10 10 i31ref)
342           (tag $tag (param f64))
343           (func (export "main")
344             ;; $g := 42
345             i32.const 42
346             global.set $g
347             ;; $m[1024] := 1
348             i32.const 1024
349             i32.const 1
350             i32.store8 $m
351             ;; $t[1] := (ref.i31 (i32.const 100))
352             i32.const 1
353             i32.const 100
354             ref.i31
355             table.set $t)
356 
357           (func (param i32)
358             local.get 0
359             global.set $g))
360         "#,
361     )?;
362 
363     let host_global = Global::new(
364         &mut store,
365         GlobalType::new(ValType::I32, Mutability::Var),
366         Val::I32(1000),
367     )?;
368     let host_func = Func::wrap(&mut store, |_caller: Caller<'_, ()>| -> i32 { 7 });
369 
370     let instance = Instance::new(
371         &mut store,
372         &module,
373         &[Extern::Global(host_global), Extern::Func(host_func)],
374     )?;
375     let func = instance.get_func(&mut store, "main").unwrap();
376     func.call(&mut store, &[], &mut [])?;
377 
378     // Nothing is exported except for `main`, yet we can still access
379     // (below).
380     let exports = instance.exports(&mut store).collect::<Vec<_>>();
381     assert_eq!(exports.len(), 1);
382     assert!(exports.into_iter().next().unwrap().into_func().is_some());
383 
384     // We can call a non-exported function.
385     let f = instance.debug_function(&mut store, 2).unwrap();
386     f.call(&mut store, &[Val::I32(1234)], &mut [])?;
387 
388     let g = instance.debug_global(&mut store, 1).unwrap();
389     assert_eq!(g.get(&mut store).unwrap_i32(), 1234);
390 
391     let m = instance.debug_memory(&mut store, 0).unwrap();
392     assert_eq!(m.data(&mut store)[1024], 1);
393 
394     let t = instance.debug_table(&mut store, 0).unwrap();
395     let t_val = t.get(&mut store, 1).unwrap();
396     let t_val = t_val.as_any().unwrap().unwrap().unwrap_i31(&store).unwrap();
397     assert_eq!(t_val.get_u32(), 100);
398 
399     let tag = instance.debug_tag(&mut store, 0).unwrap();
400     assert!(matches!(
401         tag.ty(&store).ty().param(0).unwrap(),
402         ValType::F64
403     ));
404 
405     // Check that we can access an imported global in the instance's
406     // index space.
407     let host_global_import = instance.debug_global(&mut store, 0).unwrap();
408     assert_eq!(host_global_import.get(&mut store).unwrap_i32(), 1000);
409 
410     // Check that we can call an imported function in the instance's
411     // index space.
412     let host_func_import = instance.debug_function(&mut store, 0).unwrap();
413     let mut results = [Val::I32(0)];
414     host_func_import.call(&mut store, &[], &mut results[..])?;
415     assert_eq!(results[0].unwrap_i32(), 7);
416 
417     // Check that out-of-bounds returns `None` rather than panic'ing.
418     assert!(instance.debug_global(&mut store, 2).is_none());
419 
420     Ok(())
421 }
422 
423 #[test]
424 #[cfg_attr(miri, ignore)]
425 #[cfg(target_pointer_width = "64")] // Threads not supported on 32-bit systems.
426 fn private_entity_access_shared_memory() -> wasmtime::Result<()> {
427     let mut config = Config::default();
428     config.guest_debug(true);
429     config.shared_memory(true);
430     config.wasm_threads(true);
431     let engine = Engine::new(&config)?;
432     let mut store = Store::new(&engine, ());
433     let module = Module::new(
434         &engine,
435         r#"
436         (module
437           (memory 1 1 shared))
438         "#,
439     )?;
440 
441     let instance = Instance::new(&mut store, &module, &[])?;
442 
443     let m = instance.debug_shared_memory(&mut store, 0).unwrap();
444     let unsafe_cell = &m.data()[1024];
445     assert_eq!(unsafe { *unsafe_cell.get() }, 0);
446 
447     Ok(())
448 }
449 
450 macro_rules! debug_event_checker {
451     ($ty:tt,
452      $store:tt,
453      $(
454          { $i:expr ; $pat:pat => $body:tt }
455      ),*)
456     =>
457     {
458         #[derive(Clone)]
459         struct $ty(Arc<AtomicUsize>);
460         impl $ty {
461             fn new_and_counter() -> (Self, Arc<AtomicUsize>) {
462                 let counter = Arc::new(AtomicUsize::new(0));
463                 let counter_clone = counter.clone();
464                 ($ty(counter), counter_clone)
465             }
466         }
467         impl DebugHandler for $ty {
468             type Data = ();
469             fn handle(
470                 &self,
471                 #[allow(unused_variables, reason = "macro rules")]
472                 #[allow(unused_mut, reason = "macro rules")]
473                 mut $store: StoreContextMut<'_, ()>,
474                 event: DebugEvent<'_>,
475             ) -> impl Future<Output = ()> + Send {
476                 let step = self.0.fetch_add(1, Ordering::Relaxed);
477                 async move {
478                     if false {}
479                     $(
480                         else if step == $i {
481                             match event {
482                                 $pat => {
483                                     $body;
484                                 }
485                                 _ => panic!("Incorrect event"),
486                             }
487                         }
488                     )*
489                     else {
490                         panic!("Too many steps");
491                     }
492                 }
493             }
494         }
495     }
496 }
497 
498 #[tokio::test]
499 #[cfg_attr(miri, ignore)]
500 async fn uncaught_exception_events() -> wasmtime::Result<()> {
501     let _ = env_logger::try_init();
502 
503     let (module, mut store) = get_module_and_store(
504         |config| {
505             config.async_support(true);
506             config.wasm_exceptions(true);
507         },
508         r#"
509     (module
510       (tag $t (param i32))
511       (func (export "main")
512         call 1)
513       (func
514         (local $i i32)
515         (local.set $i (i32.const 100))
516         (throw $t (i32.const 42))))
517     "#,
518     )?;
519 
520     debug_event_checker!(
521         D, store,
522         { 0 ;
523           wasmtime::DebugEvent::UncaughtExceptionThrown(e) => {
524               assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42);
525               let mut stack = store.debug_frames().expect("frame cursor must be available");
526               assert!(!stack.done());
527               assert_eq!(stack.num_locals(), 1);
528               assert_eq!(stack.local(0).unwrap_i32(), 100);
529               assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
530               assert!(!stack.done());
531               assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
532               assert!(stack.done());
533           }
534         }
535     );
536 
537     let (handler, counter) = D::new_and_counter();
538     store.set_debug_handler(handler);
539 
540     let instance = Instance::new_async(&mut store, &module, &[]).await?;
541     let func = instance.get_func(&mut store, "main").unwrap();
542     let mut results = [];
543     let result = func.call_async(&mut store, &[], &mut results).await;
544     assert!(result.is_err()); // Uncaught exception.
545     assert_eq!(counter.load(Ordering::Relaxed), 1);
546 
547     Ok(())
548 }
549 
550 #[tokio::test]
551 #[cfg_attr(miri, ignore)]
552 async fn caught_exception_events() -> wasmtime::Result<()> {
553     let _ = env_logger::try_init();
554 
555     let (module, mut store) = get_module_and_store(
556         |config| {
557             config.async_support(true);
558             config.wasm_exceptions(true);
559         },
560         r#"
561     (module
562       (tag $t (param i32))
563       (func (export "main")
564         (block $b (result i32)
565           (try_table (catch $t $b)
566             call 1)
567           i32.const 0)
568         drop)
569       (func
570         (local $i i32)
571         (local.set $i (i32.const 100))
572         (throw $t (i32.const 42))))
573     "#,
574     )?;
575 
576     debug_event_checker!(
577         D, store,
578         { 0 ;
579           wasmtime::DebugEvent::CaughtExceptionThrown(e) => {
580               assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42);
581               let mut stack = store.debug_frames().expect("frame cursor must be available");
582               assert!(!stack.done());
583               assert_eq!(stack.num_locals(), 1);
584               assert_eq!(stack.local(0).unwrap_i32(), 100);
585               assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
586               assert!(!stack.done());
587               assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
588               assert!(stack.done());
589           }
590         }
591     );
592 
593     let (handler, counter) = D::new_and_counter();
594     store.set_debug_handler(handler);
595 
596     let instance = Instance::new_async(&mut store, &module, &[]).await?;
597     let func = instance.get_func(&mut store, "main").unwrap();
598     let mut results = [];
599     func.call_async(&mut store, &[], &mut results).await?;
600     assert_eq!(counter.load(Ordering::Relaxed), 1);
601 
602     Ok(())
603 }
604 
605 #[tokio::test]
606 #[cfg_attr(miri, ignore)]
607 async fn hostcall_trap_events() -> wasmtime::Result<()> {
608     let _ = env_logger::try_init();
609 
610     let (module, mut store) = get_module_and_store(
611         |config| {
612             config.async_support(true);
613             config.wasm_exceptions(true);
614         },
615         r#"
616     (module
617       (func (export "main")
618         i32.const 0
619         i32.const 0
620         i32.div_u
621         drop))
622     "#,
623     )?;
624 
625     debug_event_checker!(
626         D, store,
627         { 0 ;
628           wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => {}
629         }
630     );
631 
632     let (handler, counter) = D::new_and_counter();
633     store.set_debug_handler(handler);
634 
635     let instance = Instance::new_async(&mut store, &module, &[]).await?;
636     let func = instance.get_func(&mut store, "main").unwrap();
637     let mut results = [];
638     let result = func.call_async(&mut store, &[], &mut results).await;
639     assert!(result.is_err()); // Uncaught trap.
640     assert_eq!(counter.load(Ordering::Relaxed), 1);
641 
642     Ok(())
643 }
644 
645 #[tokio::test]
646 #[cfg_attr(miri, ignore)]
647 async fn hostcall_error_events() -> wasmtime::Result<()> {
648     let _ = env_logger::try_init();
649 
650     let (module, mut store) = get_module_and_store(
651         |config| {
652             config.async_support(true);
653             config.wasm_exceptions(true);
654         },
655         r#"
656     (module
657       (import "" "do_a_trap" (func))
658       (func (export "main")
659         call 0))
660     "#,
661     )?;
662 
663     debug_event_checker!(
664         D, store,
665         { 0 ;
666           wasmtime::DebugEvent::HostcallError(e) => {
667               assert!(format!("{e:?}").contains("secret error message"));
668           }
669         }
670     );
671 
672     let (handler, counter) = D::new_and_counter();
673     store.set_debug_handler(handler);
674 
675     let do_a_trap = Func::wrap(
676         &mut store,
677         |_caller: Caller<'_, ()>| -> wasmtime::Result<()> {
678             Err(wasmtime::format_err!("secret error message"))
679         },
680     );
681     let instance = Instance::new_async(&mut store, &module, &[Extern::Func(do_a_trap)]).await?;
682     let func = instance.get_func(&mut store, "main").unwrap();
683     let mut results = [];
684     let result = func.call_async(&mut store, &[], &mut results).await;
685     assert!(result.is_err()); // Uncaught trap.
686     assert_eq!(counter.load(Ordering::Relaxed), 1);
687     Ok(())
688 }
689 
690 #[tokio::test]
691 #[cfg_attr(miri, ignore)]
692 async fn breakpoint_events() -> wasmtime::Result<()> {
693     let _ = env_logger::try_init();
694 
695     let (module, mut store) = get_module_and_store(
696         |config| {
697             config.async_support(true);
698             config.wasm_exceptions(true);
699         },
700         r#"
701     (module
702       (func (export "main") (param i32 i32) (result i32)
703         local.get 0
704         local.get 1
705         i32.add))
706     "#,
707     )?;
708 
709     debug_event_checker!(
710         D, store,
711         { 0 ;
712           wasmtime::DebugEvent::Breakpoint => {
713               let mut stack = store.debug_frames().expect("frame cursor must be available");
714               assert!(!stack.done());
715               assert_eq!(stack.num_locals(), 2);
716               assert_eq!(stack.local(0).unwrap_i32(), 1);
717               assert_eq!(stack.local(1).unwrap_i32(), 2);
718               let (func, pc) = stack.wasm_function_index_and_pc().unwrap();
719               assert_eq!(func.as_u32(), 0);
720               assert_eq!(pc, 0x28);
721               assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation);
722               assert!(stack.done());
723           }
724         }
725     );
726 
727     let (handler, counter) = D::new_and_counter();
728     store.set_debug_handler(handler);
729     store
730         .edit_breakpoints()
731         .unwrap()
732         .add_breakpoint(&module, 0x28)?;
733 
734     let instance = Instance::new_async(&mut store, &module, &[]).await?;
735     let func = instance.get_func(&mut store, "main").unwrap();
736     let mut results = [Val::I32(0)];
737     func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
738         .await?;
739     assert_eq!(counter.load(Ordering::Relaxed), 1);
740     assert_eq!(results[0].unwrap_i32(), 3);
741 
742     let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>();
743     assert_eq!(breakpoints.len(), 1);
744     assert!(Module::same(&breakpoints[0].module, &module));
745     assert_eq!(breakpoints[0].pc, 0x28);
746 
747     store
748         .edit_breakpoints()
749         .unwrap()
750         .remove_breakpoint(&module, 0x28)?;
751     func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
752         .await?;
753     assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above.
754     assert_eq!(results[0].unwrap_i32(), 3);
755 
756     // Enable single-step mode (on top of the breakpoint already enabled).
757     assert!(!store.is_single_step());
758     store.edit_breakpoints().unwrap().single_step(true).unwrap();
759     assert!(store.is_single_step());
760 
761     debug_event_checker!(
762         D2, store,
763         { 0 ;
764           wasmtime::DebugEvent::Breakpoint => {
765               let stack = store.debug_frames().unwrap();
766               assert!(!stack.done());
767               let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
768               assert_eq!(pc, 0x24);
769           }
770         },
771         {
772           1 ;
773           wasmtime::DebugEvent::Breakpoint => {
774               let stack = store.debug_frames().unwrap();
775               assert!(!stack.done());
776               let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
777               assert_eq!(pc, 0x26);
778           }
779         },
780         {
781           2 ;
782           wasmtime::DebugEvent::Breakpoint => {
783               let stack = store.debug_frames().unwrap();
784               assert!(!stack.done());
785               let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
786               assert_eq!(pc, 0x28);
787           }
788         },
789         {
790           3 ;
791           wasmtime::DebugEvent::Breakpoint => {
792               let stack = store.debug_frames().unwrap();
793               assert!(!stack.done());
794               let (_, pc) = stack.wasm_function_index_and_pc().unwrap();
795               assert_eq!(pc, 0x29);
796           }
797         }
798     );
799 
800     let (handler, counter) = D2::new_and_counter();
801     store.set_debug_handler(handler);
802 
803     func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
804         .await?;
805     assert_eq!(counter.load(Ordering::Relaxed), 4);
806 
807     // Re-enable individual breakpoint.
808     store
809         .edit_breakpoints()
810         .unwrap()
811         .add_breakpoint(&module, 0x28)
812         .unwrap();
813 
814     // Now disable single-stepping. The single breakpoint set above
815     // should still remain.
816     store
817         .edit_breakpoints()
818         .unwrap()
819         .single_step(false)
820         .unwrap();
821 
822     let (handler, counter) = D::new_and_counter();
823     store.set_debug_handler(handler);
824 
825     func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
826         .await?;
827     assert_eq!(counter.load(Ordering::Relaxed), 1);
828 
829     Ok(())
830 }
831 
832 #[tokio::test]
833 #[cfg_attr(miri, ignore)]
834 async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> {
835     let _ = env_logger::try_init();
836 
837     let (module, mut store) = get_module_and_store(
838         |config| {
839             config.async_support(true);
840             config.wasm_exceptions(true);
841             config.compiler_inlining(true);
842             unsafe {
843                 config.cranelift_flag_set("wasmtime_inlining_intra_module", "true");
844             }
845         },
846         r#"
847     (module
848       (func $f (export "f") (param i32 i32) (result i32)
849         local.get 0
850         local.get 1
851         i32.add)
852 
853       (func (export "main") (param i32 i32) (result i32)
854         local.get 0
855         local.get 1
856         call $f))
857     "#,
858     )?;
859 
860     debug_event_checker!(
861         D, store,
862         { 0 ;
863           wasmtime::DebugEvent::Breakpoint => {}
864         },
865         { 1 ;
866           wasmtime::DebugEvent::Breakpoint => {}
867         }
868     );
869 
870     let (handler, counter) = D::new_and_counter();
871     store.set_debug_handler(handler);
872     store
873         .edit_breakpoints()
874         .unwrap()
875         .add_breakpoint(&module, 0x2d)?; // `i32.add` in `$f`.
876 
877     let instance = Instance::new_async(&mut store, &module, &[]).await?;
878     let func_main = instance.get_func(&mut store, "main").unwrap();
879     let func_f = instance.get_func(&mut store, "f").unwrap();
880     let mut results = [Val::I32(0)];
881     // Breakpoint in `$f` should have been hit in `main` even if it
882     // was inlined.
883     func_main
884         .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
885         .await?;
886     assert_eq!(counter.load(Ordering::Relaxed), 1);
887     assert_eq!(results[0].unwrap_i32(), 3);
888 
889     // Breakpoint in `$f` should be hit when called directly, too.
890     func_f
891         .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
892         .await?;
893     assert_eq!(counter.load(Ordering::Relaxed), 2);
894     assert_eq!(results[0].unwrap_i32(), 3);
895 
896     Ok(())
897 }
898 
899 #[tokio::test]
900 #[cfg_attr(miri, ignore)]
901 async fn epoch_events() -> wasmtime::Result<()> {
902     let _ = env_logger::try_init();
903 
904     let (module, mut store) = get_module_and_store(
905         |config| {
906             config.async_support(true);
907             config.epoch_interruption(true);
908         },
909         r#"
910     (module
911       (func $f (export "f") (param i32 i32) (result i32)
912         local.get 0
913         local.get 1
914         i32.add))
915     "#,
916     )?;
917 
918     debug_event_checker!(
919         D, store,
920         { 0 ;
921           wasmtime::DebugEvent::EpochYield => {}
922         }
923     );
924 
925     let (handler, counter) = D::new_and_counter();
926     store.set_debug_handler(handler);
927 
928     store.set_epoch_deadline(1);
929     store.epoch_deadline_async_yield_and_update(1);
930     store.engine().increment_epoch();
931 
932     let instance = Instance::new_async(&mut store, &module, &[]).await?;
933     let func_f = instance.get_func(&mut store, "f").unwrap();
934     let mut results = [Val::I32(0)];
935     func_f
936         .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
937         .await?;
938     assert_eq!(counter.load(Ordering::Relaxed), 1);
939     assert_eq!(results[0].unwrap_i32(), 3);
940 
941     Ok(())
942 }
943