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