1 //! Tests for instrumentation-based debugging.
2
3 use std::sync::atomic::{AtomicUsize, Ordering};
4 use std::sync::{Arc, Mutex};
5 use wasmtime::{
6 AsContextMut, Caller, Config, DebugEvent, DebugHandler, Engine, Extern, FrameHandle, Func,
7 Global, GlobalType, Instance, Module, ModulePC, Mutability, Store, StoreContextMut, Val,
8 ValType,
9 };
10
11 use crate::async_functions::PollOnce;
12
13 #[test]
debugging_does_not_work_with_signal_based_traps()14 fn debugging_does_not_work_with_signal_based_traps() {
15 let mut config = Config::default();
16 config.guest_debug(true).signals_based_traps(true);
17 let err = Engine::new(&config).expect_err("invalid config should produce an error");
18 assert!(format!("{err:?}").contains("cannot use signals-based traps"));
19 }
20
21 #[test]
debugging_apis_are_denied_without_debugging() -> wasmtime::Result<()>22 fn debugging_apis_are_denied_without_debugging() -> wasmtime::Result<()> {
23 let mut config = Config::default();
24 config.guest_debug(false);
25 let engine = Engine::new(&config)?;
26 let module = Module::new(&engine, "(module (global $g (mut i32) (i32.const 0)))")?;
27 let mut store = Store::new(&engine, ());
28 let instance = Instance::new(&mut store, &module, &[])?;
29
30 assert!(store.debug_exit_frames().next().is_none());
31 assert!(instance.debug_global(&mut store, 0).is_none());
32
33 Ok(())
34 }
35
get_module_and_store<C: Fn(&mut Config)>( c: C, wat: &str, ) -> wasmtime::Result<(Module, Store<()>)>36 fn get_module_and_store<C: Fn(&mut Config)>(
37 c: C,
38 wat: &str,
39 ) -> wasmtime::Result<(Module, Store<()>)> {
40 let mut config = Config::default();
41 config.guest_debug(true);
42 config.wasm_exceptions(true);
43 c(&mut config);
44 let engine = Engine::new(&config)?;
45 let module = Module::new(&engine, wat)?;
46 Ok((module, Store::new(&engine, ())))
47 }
48
test_stack_values< C: Fn(&mut Config), F: Fn(Caller<'_, ()>) -> wasmtime::Result<()> + Send + Sync + 'static, >( wat: &str, c: C, f: F, ) -> wasmtime::Result<()>49 fn test_stack_values<
50 C: Fn(&mut Config),
51 F: Fn(Caller<'_, ()>) -> wasmtime::Result<()> + Send + Sync + 'static,
52 >(
53 wat: &str,
54 c: C,
55 f: F,
56 ) -> wasmtime::Result<()> {
57 let (module, mut store) = get_module_and_store(c, wat)?;
58 let func = Func::wrap(&mut store, move |caller: Caller<'_, ()>| {
59 f(caller)?;
60 Ok(())
61 });
62 let instance = Instance::new(&mut store, &module, &[Extern::Func(func)])?;
63 let mut results = [];
64 instance
65 .get_func(&mut store, "main")
66 .unwrap()
67 .call(&mut store, &[], &mut results)?;
68
69 Ok(())
70 }
71
72 #[test]
73 #[cfg_attr(miri, ignore)]
stack_values_two_frames() -> wasmtime::Result<()>74 fn stack_values_two_frames() -> wasmtime::Result<()> {
75 let _ = env_logger::try_init();
76
77 for inlining in [false, true] {
78 test_stack_values(
79 r#"
80 (module
81 (import "" "host" (func))
82 (func (export "main")
83 i32.const 1
84 i32.const 2
85 call 2
86 drop)
87 (func (param i32 i32) (result i32)
88 local.get 0
89 local.get 1
90 call 0
91 i32.add))
92 "#,
93 |config| {
94 config.compiler_inlining(inlining);
95 if inlining {
96 unsafe {
97 config.cranelift_flag_set("wasmtime_inlining_intra_module", "true");
98 }
99 }
100 },
101 |mut caller: Caller<'_, ()>| {
102 let stack = caller.debug_exit_frames().next().unwrap();
103 assert_eq!(
104 stack
105 .wasm_function_index_and_pc(&mut caller)?
106 .unwrap()
107 .0
108 .as_u32(),
109 1
110 );
111 assert_eq!(
112 stack
113 .wasm_function_index_and_pc(&mut caller)?
114 .unwrap()
115 .1
116 .raw(),
117 67
118 );
119
120 assert_eq!(stack.num_locals(&mut caller)?, 2);
121 assert_eq!(stack.num_stacks(&mut caller)?, 2);
122 assert_eq!(stack.local(&mut caller, 0)?.unwrap_i32(), 1);
123 assert_eq!(stack.local(&mut caller, 1)?.unwrap_i32(), 2);
124 assert_eq!(stack.stack(&mut caller, 0)?.unwrap_i32(), 1);
125 assert_eq!(stack.stack(&mut caller, 1)?.unwrap_i32(), 2);
126
127 let stack = stack.parent(&mut caller)?.unwrap();
128 assert_eq!(
129 stack
130 .wasm_function_index_and_pc(&mut caller)?
131 .unwrap()
132 .0
133 .as_u32(),
134 0
135 );
136 assert_eq!(
137 stack
138 .wasm_function_index_and_pc(&mut caller)?
139 .unwrap()
140 .1
141 .raw(),
142 57
143 );
144
145 let stack = stack.parent(&mut caller)?;
146 assert!(stack.is_none());
147 Ok(())
148 },
149 )?;
150 }
151 Ok(())
152 }
153
154 #[test]
155 #[cfg_attr(miri, ignore)]
stack_values_exceptions() -> wasmtime::Result<()>156 fn stack_values_exceptions() -> wasmtime::Result<()> {
157 test_stack_values(
158 r#"
159 (module
160 (tag $t (param i32))
161 (import "" "host" (func))
162 (func (export "main")
163 (block $b (result i32)
164 (try_table (catch $t $b)
165 (throw $t (i32.const 42)))
166 i32.const 0)
167 (call 0)
168 (drop)))
169 "#,
170 |_config| {},
171 |mut caller: Caller<'_, ()>| {
172 let stack = caller.debug_exit_frames().next().unwrap();
173 assert_eq!(stack.num_stacks(&mut caller)?, 1);
174 assert_eq!(stack.stack(&mut caller, 0)?.unwrap_i32(), 42);
175 let stack = stack.parent(&mut caller)?;
176 assert!(stack.is_none());
177 Ok(())
178 },
179 )
180 }
181
182 #[test]
183 #[cfg_attr(miri, ignore)]
stack_values_dead_gc_ref() -> wasmtime::Result<()>184 fn stack_values_dead_gc_ref() -> wasmtime::Result<()> {
185 test_stack_values(
186 r#"
187 (module
188 (type $s (struct))
189 (import "" "host" (func))
190 (func (export "main")
191 (struct.new $s)
192 (call 0)
193 (drop)))
194 "#,
195 |config| {
196 config.wasm_gc(true);
197 },
198 |mut caller: Caller<'_, ()>| {
199 let stack = caller.debug_exit_frames().next().unwrap();
200 assert_eq!(stack.num_stacks(&mut caller)?, 1);
201 assert!(stack.stack(&mut caller, 0)?.unwrap_anyref().is_some());
202 let stack = stack.parent(&mut caller)?;
203 assert!(stack.is_none());
204 Ok(())
205 },
206 )
207 }
208
209 #[test]
210 #[cfg_attr(miri, ignore)]
gc_access_during_call() -> wasmtime::Result<()>211 fn gc_access_during_call() -> wasmtime::Result<()> {
212 test_stack_values(
213 r#"
214 (module
215 (type $s (struct (field i32)))
216 (import "" "host" (func))
217 (func (export "main")
218 (local $l (ref null $s))
219 (local.set $l (struct.new $s (i32.const 42)))
220 (call 0)))
221 "#,
222 |config| {
223 config.wasm_gc(true);
224 },
225 |mut caller: Caller<'_, ()>| {
226 let stack = caller.debug_exit_frames().next().unwrap();
227
228 // Do a GC while we hold the stack cursor.
229 caller.as_context_mut().gc(None).unwrap();
230
231 assert_eq!(stack.num_stacks(&mut caller)?, 0);
232 assert_eq!(stack.num_locals(&mut caller)?, 1);
233 // Note that this struct is dead during the call, and the
234 // ref could otherwise be optimized away (no longer in the
235 // stackmap at this point); but we verify it is still
236 // alive here because it is rooted in the
237 // debug-instrumentation slot.
238 let s = stack
239 .local(&mut caller, 0)?
240 .unwrap_any_ref()
241 .unwrap()
242 .unwrap_struct(&caller)
243 .unwrap();
244 assert_eq!(s.field(&mut caller, 0).unwrap().unwrap_i32(), 42);
245 let stack = stack.parent(&mut caller)?;
246 assert!(stack.is_none());
247 Ok(())
248 },
249 )
250 }
251
252 #[test]
253 #[cfg_attr(miri, ignore)]
stack_values_two_activations() -> wasmtime::Result<()>254 fn stack_values_two_activations() -> wasmtime::Result<()> {
255 let _ = env_logger::try_init();
256
257 let mut config = Config::default();
258 config.guest_debug(true);
259 config.wasm_exceptions(true);
260 let engine = Engine::new(&config)?;
261 let module1 = Module::new(
262 &engine,
263 r#"
264 (module
265 (import "" "host1" (func (param i32 i32) (result i32)))
266 (func (export "main") (result i32)
267 i32.const 1
268 i32.const 2
269 call 0))
270 "#,
271 )?;
272 let module2 = Module::new(
273 &engine,
274 r#"
275 (module
276 (import "" "host2" (func))
277 (func (export "inner") (param i32 i32) (result i32)
278 local.get 0
279 local.get 1
280 call 0
281 i32.add))
282 "#,
283 )?;
284 let mut store = Store::new(&engine, ());
285
286 let module1_clone = module1.clone();
287 let module2_clone = module2.clone();
288 let host2 = Func::wrap(&mut store, move |mut caller: Caller<'_, ()>| {
289 let exits = caller.debug_exit_frames().collect::<Vec<_>>();
290 assert_eq!(exits.len(), 2);
291 let stack = exits[0].clone();
292 assert_eq!(
293 stack
294 .wasm_function_index_and_pc(&mut caller)?
295 .unwrap()
296 .0
297 .as_u32(),
298 0
299 );
300 assert_eq!(
301 stack
302 .wasm_function_index_and_pc(&mut caller)?
303 .unwrap()
304 .1
305 .raw(),
306 58
307 );
308 assert!(Module::same(
309 stack.module(&mut caller)?.unwrap(),
310 &module2_clone
311 ));
312 assert_eq!(stack.num_locals(&mut caller)?, 2);
313 assert_eq!(stack.num_stacks(&mut caller)?, 2);
314 assert_eq!(stack.local(&mut caller, 0)?.unwrap_i32(), 1);
315 assert_eq!(stack.local(&mut caller, 1)?.unwrap_i32(), 2);
316 assert_eq!(stack.stack(&mut caller, 0)?.unwrap_i32(), 1);
317 assert_eq!(stack.stack(&mut caller, 1)?.unwrap_i32(), 2);
318 let inner_instance = stack.instance(&mut caller)?;
319
320 let stack = stack.parent(&mut caller)?;
321 assert!(stack.is_none());
322
323 let stack = exits[1].clone();
324 assert_eq!(
325 stack
326 .wasm_function_index_and_pc(&mut caller)?
327 .unwrap()
328 .0
329 .as_u32(),
330 0
331 );
332 assert_eq!(
333 stack
334 .wasm_function_index_and_pc(&mut caller)?
335 .unwrap()
336 .1
337 .raw(),
338 58
339 );
340 assert!(Module::same(
341 stack.module(&mut caller)?.unwrap(),
342 &module1_clone
343 ));
344 assert_eq!(stack.num_locals(&mut caller)?, 0);
345 assert_eq!(stack.num_stacks(&mut caller)?, 2);
346 assert_eq!(stack.stack(&mut caller, 0)?.unwrap_i32(), 1);
347 assert_eq!(stack.stack(&mut caller, 1)?.unwrap_i32(), 2);
348 let outer_instance = stack.instance(&mut caller)?;
349 assert_ne!(inner_instance, outer_instance);
350
351 let stack = stack.parent(&mut caller)?;
352 assert!(stack.is_none());
353
354 Ok(())
355 });
356
357 let instance2 = Instance::new(&mut store, &module2, &[Extern::Func(host2)])?;
358 let inner = instance2.get_func(&mut store, "inner").unwrap();
359
360 let host1 = Func::wrap(
361 &mut store,
362 move |mut caller: Caller<'_, ()>, a: i32, b: i32| -> i32 {
363 let mut results = [Val::I32(0)];
364 inner
365 .call(&mut caller, &[Val::I32(a), Val::I32(b)], &mut results[..])
366 .unwrap();
367 results[0].unwrap_i32()
368 },
369 );
370
371 let instance1 = Instance::new(&mut store, &module1, &[Extern::Func(host1)])?;
372 let main = instance1.get_func(&mut store, "main").unwrap();
373
374 let mut results = [Val::I32(0)];
375 main.call(&mut store, &[], &mut results)?;
376 assert_eq!(results[0].unwrap_i32(), 3);
377 Ok(())
378 }
379
380 #[test]
381 #[cfg_attr(miri, ignore)]
debug_frames_on_store_with_no_wasm_activation() -> wasmtime::Result<()>382 fn debug_frames_on_store_with_no_wasm_activation() -> wasmtime::Result<()> {
383 let mut config = Config::default();
384 config.guest_debug(true);
385 let engine = Engine::new(&config)?;
386 let mut store = Store::new(&engine, ());
387 let frames = store.debug_exit_frames().collect::<Vec<_>>();
388 assert_eq!(frames.len(), 0);
389 Ok(())
390 }
391
392 #[test]
393 #[cfg_attr(miri, ignore)]
private_entity_access() -> wasmtime::Result<()>394 fn private_entity_access() -> wasmtime::Result<()> {
395 let mut config = Config::default();
396 config.guest_debug(true);
397 config.wasm_gc(true);
398 config.gc_support(true);
399 config.wasm_exceptions(true);
400 let engine = Engine::new(&config)?;
401 let mut store = Store::new(&engine, ());
402 let module = Module::new(
403 &engine,
404 r#"
405 (module
406 (import "" "i" (global (mut i32)))
407 (import "" "f" (func (result i32)))
408 (global $g (mut i32) (i32.const 0))
409 (memory $m 1 1)
410 (table $t 10 10 i31ref)
411 (tag $tag (param f64))
412 (func (export "main")
413 ;; $g := 42
414 i32.const 42
415 global.set $g
416 ;; $m[1024] := 1
417 i32.const 1024
418 i32.const 1
419 i32.store8 $m
420 ;; $t[1] := (ref.i31 (i32.const 100))
421 i32.const 1
422 i32.const 100
423 ref.i31
424 table.set $t)
425
426 (func (param i32)
427 local.get 0
428 global.set $g))
429 "#,
430 )?;
431
432 let host_global = Global::new(
433 &mut store,
434 GlobalType::new(ValType::I32, Mutability::Var),
435 Val::I32(1000),
436 )?;
437 let host_func = Func::wrap(&mut store, |_caller: Caller<'_, ()>| -> i32 { 7 });
438
439 let instance = Instance::new(
440 &mut store,
441 &module,
442 &[Extern::Global(host_global), Extern::Func(host_func)],
443 )?;
444 let func = instance.get_func(&mut store, "main").unwrap();
445 func.call(&mut store, &[], &mut [])?;
446
447 // Nothing is exported except for `main`, yet we can still access
448 // (below).
449 let exports = instance.exports(&mut store).collect::<Vec<_>>();
450 assert_eq!(exports.len(), 1);
451 assert!(exports.into_iter().next().unwrap().into_func().is_some());
452
453 // We can call a non-exported function.
454 let f = instance.debug_function(&mut store, 2).unwrap();
455 f.call(&mut store, &[Val::I32(1234)], &mut [])?;
456
457 let g = instance.debug_global(&mut store, 1).unwrap();
458 assert_eq!(g.get(&mut store).unwrap_i32(), 1234);
459
460 let m = instance.debug_memory(&mut store, 0).unwrap();
461 assert_eq!(m.data(&mut store)[1024], 1);
462
463 let t = instance.debug_table(&mut store, 0).unwrap();
464 let t_val = t.get(&mut store, 1).unwrap();
465 let t_val = t_val.as_any().unwrap().unwrap().unwrap_i31(&store).unwrap();
466 assert_eq!(t_val.get_u32(), 100);
467
468 let tag = instance.debug_tag(&mut store, 0).unwrap();
469 assert!(matches!(
470 tag.ty(&store).ty().param(0).unwrap(),
471 ValType::F64
472 ));
473
474 // Check that we can access an imported global in the instance's
475 // index space.
476 let host_global_import = instance.debug_global(&mut store, 0).unwrap();
477 assert_eq!(host_global_import.get(&mut store).unwrap_i32(), 1000);
478
479 // Check that we can call an imported function in the instance's
480 // index space.
481 let host_func_import = instance.debug_function(&mut store, 0).unwrap();
482 let mut results = [Val::I32(0)];
483 host_func_import.call(&mut store, &[], &mut results[..])?;
484 assert_eq!(results[0].unwrap_i32(), 7);
485
486 // Check that out-of-bounds returns `None` rather than panic'ing.
487 assert!(instance.debug_global(&mut store, 2).is_none());
488
489 Ok(())
490 }
491
492 #[test]
493 #[cfg_attr(miri, ignore)]
494 #[cfg(target_pointer_width = "64")] // Threads not supported on 32-bit systems.
private_entity_access_shared_memory() -> wasmtime::Result<()>495 fn private_entity_access_shared_memory() -> wasmtime::Result<()> {
496 let mut config = Config::default();
497 config.guest_debug(true);
498 config.shared_memory(true);
499 config.wasm_threads(true);
500 let engine = Engine::new(&config)?;
501 let mut store = Store::new(&engine, ());
502 let module = Module::new(
503 &engine,
504 r#"
505 (module
506 (memory 1 1 shared))
507 "#,
508 )?;
509
510 let instance = Instance::new(&mut store, &module, &[])?;
511
512 let m = instance.debug_shared_memory(&mut store, 0).unwrap();
513 let unsafe_cell = &m.data()[1024];
514 assert_eq!(unsafe { *unsafe_cell.get() }, 0);
515
516 Ok(())
517 }
518
519 #[test]
520 #[cfg_attr(miri, ignore)]
all_instances_and_modules_in_store() -> wasmtime::Result<()>521 fn all_instances_and_modules_in_store() -> wasmtime::Result<()> {
522 let mut config = Config::default();
523 config.guest_debug(true);
524 let engine = Engine::new(&config)?;
525 let mut store = Store::new(&engine, ());
526 let m1 = Module::new(
527 &engine,
528 r#"
529 (module (func (param i32) (result i32) (local.get 0)))
530 "#,
531 )?;
532 let m2 = Module::new(
533 &engine,
534 r#"
535 (module (func (param i32) (result i32) (local.get 0)))
536 "#,
537 )?;
538 let i1 = Instance::new(&mut store, &m1, &[])?;
539 let i2 = Instance::new(&mut store, &m2, &[])?;
540
541 let instances = store.debug_all_instances();
542 let modules = store.debug_all_modules();
543 assert_eq!(instances.len(), 2);
544 assert_eq!(modules.len(), 2);
545 assert!(
546 (Module::same(&modules[0], &m1) && Module::same(&modules[1], &m2))
547 || (Module::same(&modules[1], &m1) && Module::same(&modules[0], &m2))
548 );
549 assert!(instances[0] == i1);
550 assert!(instances[1] == i2);
551 Ok(())
552 }
553
554 macro_rules! debug_event_checker {
555 ($ty:tt,
556 $store:tt,
557 $(
558 { $i:expr ; $pat:pat => $body:tt }
559 ),*)
560 =>
561 {
562 #[derive(Clone)]
563 struct $ty(Arc<AtomicUsize>);
564 impl $ty {
565 fn new_and_counter() -> (Self, Arc<AtomicUsize>) {
566 let counter = Arc::new(AtomicUsize::new(0));
567 let counter_clone = counter.clone();
568 ($ty(counter), counter_clone)
569 }
570 }
571 impl DebugHandler for $ty {
572 type Data = ();
573 fn handle(
574 &self,
575 #[allow(unused_variables, reason = "macro rules")]
576 #[allow(unused_mut, reason = "macro rules")]
577 mut $store: StoreContextMut<'_, ()>,
578 event: DebugEvent<'_>,
579 ) -> impl Future<Output = ()> + Send {
580 let step = self.0.fetch_add(1, Ordering::Relaxed);
581 async move {
582 if false {}
583 $(
584 else if step == $i {
585 match event {
586 $pat => {
587 $body;
588 }
589 _ => panic!("Incorrect event"),
590 }
591 }
592 )*
593 else {
594 panic!("Too many steps");
595 }
596 }
597 }
598 }
599 }
600 }
601
602 #[tokio::test]
603 #[cfg_attr(miri, ignore)]
uncaught_exception_events() -> wasmtime::Result<()>604 async fn uncaught_exception_events() -> wasmtime::Result<()> {
605 let _ = env_logger::try_init();
606
607 let (module, mut store) = get_module_and_store(
608 |config| {
609 config.wasm_exceptions(true);
610 },
611 r#"
612 (module
613 (tag $t (param i32))
614 (func (export "main")
615 call 1)
616 (func
617 (local $i i32)
618 (local.set $i (i32.const 100))
619 (throw $t (i32.const 42))))
620 "#,
621 )?;
622
623 debug_event_checker!(
624 D, store,
625 { 0 ;
626 wasmtime::DebugEvent::UncaughtExceptionThrown(e) => {
627 assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42);
628 let stack = store.debug_exit_frames().next().unwrap();
629 assert_eq!(stack.num_locals(&mut store).unwrap(), 1);
630 assert_eq!(stack.local(&mut store, 0).unwrap().unwrap_i32(), 100);
631 let stack = stack.parent(&mut store).unwrap().unwrap();
632 let stack = stack.parent(&mut store).unwrap();
633 assert!(stack.is_none());
634 }
635 }
636 );
637
638 let (handler, counter) = D::new_and_counter();
639 store.set_debug_handler(handler);
640
641 let instance = Instance::new_async(&mut store, &module, &[]).await?;
642 let func = instance.get_func(&mut store, "main").unwrap();
643 let mut results = [];
644 let result = func.call_async(&mut store, &[], &mut results).await;
645 assert!(result.is_err()); // Uncaught exception.
646 assert_eq!(counter.load(Ordering::Relaxed), 1);
647
648 Ok(())
649 }
650
651 #[tokio::test]
652 #[cfg_attr(miri, ignore)]
caught_exception_events() -> wasmtime::Result<()>653 async fn caught_exception_events() -> wasmtime::Result<()> {
654 let _ = env_logger::try_init();
655
656 let (module, mut store) = get_module_and_store(
657 |config| {
658 config.wasm_exceptions(true);
659 },
660 r#"
661 (module
662 (tag $t (param i32))
663 (func (export "main")
664 (block $b (result i32)
665 (try_table (catch $t $b)
666 call 1)
667 i32.const 0)
668 drop)
669 (func
670 (local $i i32)
671 (local.set $i (i32.const 100))
672 (throw $t (i32.const 42))))
673 "#,
674 )?;
675
676 debug_event_checker!(
677 D, store,
678 { 0 ;
679 wasmtime::DebugEvent::CaughtExceptionThrown(e) => {
680 assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42);
681 let stack = store.debug_exit_frames().next().unwrap();
682 assert_eq!(stack.num_locals(&mut store).unwrap(), 1);
683 assert_eq!(stack.local(&mut store, 0).unwrap().unwrap_i32(), 100);
684 let stack = stack.parent(&mut store).unwrap().unwrap();
685 let stack = stack.parent(&mut store).unwrap();
686 assert!(stack.is_none());
687 }
688 }
689 );
690
691 let (handler, counter) = D::new_and_counter();
692 store.set_debug_handler(handler);
693
694 let instance = Instance::new_async(&mut store, &module, &[]).await?;
695 let func = instance.get_func(&mut store, "main").unwrap();
696 let mut results = [];
697 func.call_async(&mut store, &[], &mut results).await?;
698 assert_eq!(counter.load(Ordering::Relaxed), 1);
699
700 Ok(())
701 }
702
703 #[tokio::test]
704 #[cfg_attr(miri, ignore)]
hostcall_trap_events() -> wasmtime::Result<()>705 async fn hostcall_trap_events() -> wasmtime::Result<()> {
706 let _ = env_logger::try_init();
707
708 let (module, mut store) = get_module_and_store(
709 |config| {
710 config.wasm_exceptions(true);
711 },
712 r#"
713 (module
714 (func (export "main") (result i32)
715 i32.const 0
716 i32.const 0
717 i32.div_u
718 drop
719 i32.const 42))
720 "#,
721 )?;
722
723 debug_event_checker!(
724 D, store,
725 { 0 ;
726 wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => {
727 let frame = store.debug_exit_frames().next().unwrap();
728 let (_func, pc) = frame.wasm_function_index_and_pc(&mut store).unwrap().unwrap();
729 assert_eq!(pc.raw(), 0x26);
730 }
731 }
732 );
733
734 let (handler, counter) = D::new_and_counter();
735 store.set_debug_handler(handler);
736
737 let instance = Instance::new_async(&mut store, &module, &[]).await?;
738 let func = instance.get_func(&mut store, "main").unwrap();
739 let mut results = [Val::I32(0)];
740 let result = func.call_async(&mut store, &[], &mut results).await;
741 assert!(result.is_err()); // Uncaught trap.
742 assert_eq!(counter.load(Ordering::Relaxed), 1);
743
744 Ok(())
745 }
746
747 #[tokio::test]
748 #[cfg_attr(miri, ignore)]
hostcall_error_events() -> wasmtime::Result<()>749 async fn hostcall_error_events() -> wasmtime::Result<()> {
750 let _ = env_logger::try_init();
751
752 let (module, mut store) = get_module_and_store(
753 |config| {
754 config.wasm_exceptions(true);
755 },
756 r#"
757 (module
758 (import "" "do_a_trap" (func))
759 (func (export "main")
760 call 0))
761 "#,
762 )?;
763
764 debug_event_checker!(
765 D, store,
766 { 0 ;
767 wasmtime::DebugEvent::HostcallError(e) => {
768 assert!(format!("{e:?}").contains("secret error message"));
769 }
770 }
771 );
772
773 let (handler, counter) = D::new_and_counter();
774 store.set_debug_handler(handler);
775
776 let do_a_trap = Func::wrap(
777 &mut store,
778 |_caller: Caller<'_, ()>| -> wasmtime::Result<()> {
779 Err(wasmtime::format_err!("secret error message"))
780 },
781 );
782 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(do_a_trap)]).await?;
783 let func = instance.get_func(&mut store, "main").unwrap();
784 let mut results = [];
785 let result = func.call_async(&mut store, &[], &mut results).await;
786 assert!(result.is_err()); // Uncaught trap.
787 assert_eq!(counter.load(Ordering::Relaxed), 1);
788 Ok(())
789 }
790
791 #[tokio::test]
792 #[cfg_attr(miri, ignore)]
breakpoint_events() -> wasmtime::Result<()>793 async fn breakpoint_events() -> wasmtime::Result<()> {
794 let _ = env_logger::try_init();
795
796 let (module, mut store) = get_module_and_store(
797 |config| {
798 config.wasm_exceptions(true);
799 },
800 r#"
801 (module
802 (func (export "main") (param i32 i32) (result i32)
803 local.get 0
804 local.get 1
805 i32.add))
806 "#,
807 )?;
808
809 debug_event_checker!(
810 D, store,
811 { 0 ;
812 wasmtime::DebugEvent::Breakpoint => {
813 let stack = store.debug_exit_frames().next().unwrap();
814 assert_eq!(stack.num_locals(&mut store).unwrap(), 2);
815 assert_eq!(stack.local(&mut store, 0).unwrap().unwrap_i32(), 1);
816 assert_eq!(stack.local(&mut store, 1).unwrap().unwrap_i32(), 2);
817 let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap();
818 assert_eq!(func.as_u32(), 0);
819 assert_eq!(pc.raw(), 0x28);
820 let stack = stack.parent(&mut store).unwrap();
821 assert!(stack.is_none());
822 }
823 }
824 );
825
826 let (handler, counter) = D::new_and_counter();
827 store.set_debug_handler(handler);
828 store
829 .edit_breakpoints()
830 .unwrap()
831 .add_breakpoint(&module, ModulePC::new(0x28))?;
832
833 let instance = Instance::new_async(&mut store, &module, &[]).await?;
834 let func = instance.get_func(&mut store, "main").unwrap();
835 let mut results = [Val::I32(0)];
836 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
837 .await?;
838 assert_eq!(counter.load(Ordering::Relaxed), 1);
839 assert_eq!(results[0].unwrap_i32(), 3);
840
841 let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>();
842 assert_eq!(breakpoints.len(), 1);
843 assert!(Module::same(&breakpoints[0].module, &module));
844 assert_eq!(breakpoints[0].pc, ModulePC::new(0x28));
845
846 store
847 .edit_breakpoints()
848 .unwrap()
849 .remove_breakpoint(&module, ModulePC::new(0x28))?;
850 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
851 .await?;
852 assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above.
853 assert_eq!(results[0].unwrap_i32(), 3);
854
855 // Enable single-step mode (on top of the breakpoint already enabled).
856 assert!(!store.is_single_step());
857 store.edit_breakpoints().unwrap().single_step(true).unwrap();
858 assert!(store.is_single_step());
859
860 debug_event_checker!(
861 D2, store,
862 { 0 ;
863 wasmtime::DebugEvent::Breakpoint => {
864 let stack = store.debug_exit_frames().next().unwrap();
865 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap();
866 assert_eq!(pc.raw(), 0x24);
867 }
868 },
869 {
870 1 ;
871 wasmtime::DebugEvent::Breakpoint => {
872 let stack = store.debug_exit_frames().next().unwrap();
873 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap();
874 assert_eq!(pc.raw(), 0x26);
875 }
876 },
877 {
878 2 ;
879 wasmtime::DebugEvent::Breakpoint => {
880 let stack = store.debug_exit_frames().next().unwrap();
881 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap();
882 assert_eq!(pc.raw(), 0x28);
883 }
884 },
885 {
886 3 ;
887 wasmtime::DebugEvent::Breakpoint => {
888 let stack = store.debug_exit_frames().next().unwrap();
889 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap();
890 assert_eq!(pc.raw(), 0x29);
891 }
892 }
893 );
894
895 let (handler, counter) = D2::new_and_counter();
896 store.set_debug_handler(handler);
897
898 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
899 .await?;
900 assert_eq!(counter.load(Ordering::Relaxed), 4);
901
902 // Re-enable individual breakpoint.
903 store
904 .edit_breakpoints()
905 .unwrap()
906 .add_breakpoint(&module, ModulePC::new(0x28))
907 .unwrap();
908
909 // Now disable single-stepping. The single breakpoint set above
910 // should still remain.
911 store
912 .edit_breakpoints()
913 .unwrap()
914 .single_step(false)
915 .unwrap();
916
917 let (handler, counter) = D::new_and_counter();
918 store.set_debug_handler(handler);
919
920 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
921 .await?;
922 assert_eq!(counter.load(Ordering::Relaxed), 1);
923
924 Ok(())
925 }
926
927 #[tokio::test]
928 #[cfg_attr(miri, ignore)]
breakpoints_in_inlined_code() -> wasmtime::Result<()>929 async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> {
930 let _ = env_logger::try_init();
931
932 let (module, mut store) = get_module_and_store(
933 |config| {
934 config.wasm_exceptions(true);
935 config.compiler_inlining(true);
936 unsafe {
937 config.cranelift_flag_set("wasmtime_inlining_intra_module", "true");
938 }
939 },
940 r#"
941 (module
942 (func $f (export "f") (param i32 i32) (result i32)
943 local.get 0
944 local.get 1
945 i32.add)
946
947 (func (export "main") (param i32 i32) (result i32)
948 local.get 0
949 local.get 1
950 call $f))
951 "#,
952 )?;
953
954 debug_event_checker!(
955 D, store,
956 { 0 ;
957 wasmtime::DebugEvent::Breakpoint => {}
958 },
959 { 1 ;
960 wasmtime::DebugEvent::Breakpoint => {}
961 }
962 );
963
964 let (handler, counter) = D::new_and_counter();
965 store.set_debug_handler(handler);
966 store
967 .edit_breakpoints()
968 .unwrap()
969 .add_breakpoint(&module, ModulePC::new(0x2d))?; // `i32.add` in `$f`.
970
971 let instance = Instance::new_async(&mut store, &module, &[]).await?;
972 let func_main = instance.get_func(&mut store, "main").unwrap();
973 let func_f = instance.get_func(&mut store, "f").unwrap();
974 let mut results = [Val::I32(0)];
975 // Breakpoint in `$f` should have been hit in `main` even if it
976 // was inlined.
977 func_main
978 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
979 .await?;
980 assert_eq!(counter.load(Ordering::Relaxed), 1);
981 assert_eq!(results[0].unwrap_i32(), 3);
982
983 // Breakpoint in `$f` should be hit when called directly, too.
984 func_f
985 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
986 .await?;
987 assert_eq!(counter.load(Ordering::Relaxed), 2);
988 assert_eq!(results[0].unwrap_i32(), 3);
989
990 Ok(())
991 }
992
993 #[tokio::test]
994 #[cfg_attr(miri, ignore)]
epoch_events() -> wasmtime::Result<()>995 async fn epoch_events() -> wasmtime::Result<()> {
996 let _ = env_logger::try_init();
997
998 let (module, mut store) = get_module_and_store(
999 |config| {
1000 config.epoch_interruption(true);
1001 },
1002 r#"
1003 (module
1004 (func $f (export "f") (param i32 i32) (result i32)
1005 local.get 0
1006 local.get 1
1007 i32.add))
1008 "#,
1009 )?;
1010
1011 debug_event_checker!(
1012 D, store,
1013 { 0 ;
1014 wasmtime::DebugEvent::EpochYield => {}
1015 }
1016 );
1017
1018 let (handler, counter) = D::new_and_counter();
1019 store.set_debug_handler(handler);
1020
1021 store.set_epoch_deadline(1);
1022 store.epoch_deadline_async_yield_and_update(1);
1023 store.engine().increment_epoch();
1024
1025 let instance = Instance::new_async(&mut store, &module, &[]).await?;
1026 let func_f = instance.get_func(&mut store, "f").unwrap();
1027 let mut results = [Val::I32(0)];
1028 func_f
1029 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
1030 .await?;
1031 assert_eq!(counter.load(Ordering::Relaxed), 1);
1032 assert_eq!(results[0].unwrap_i32(), 3);
1033
1034 Ok(())
1035 }
1036
1037 #[tokio::test]
1038 #[cfg_attr(miri, ignore)]
invalidated_frame_handles() -> wasmtime::Result<()>1039 async fn invalidated_frame_handles() -> wasmtime::Result<()> {
1040 let (module, mut store) = get_module_and_store(
1041 |_config| {},
1042 r#"
1043 (module
1044 (import "" "" (func))
1045 (func (export "main")
1046 (local i32 i32)
1047 i32.const 1
1048 local.set 0
1049 i32.const 2
1050 local.set 1
1051 call 2
1052 call 0)
1053 (func
1054 (local i32 i32)
1055 i32.const 3
1056 local.set 0
1057 i32.const 4
1058 local.set 1
1059 call 0))
1060 "#,
1061 )?;
1062
1063 let handle: Arc<Mutex<Option<FrameHandle>>> = Arc::new(Mutex::new(None));
1064
1065 let hostfunc = Func::wrap_async(&mut store, move |mut caller, _args: ()| {
1066 let handle = handle.clone();
1067 Box::new(async move {
1068 let frame = handle.lock().unwrap().take();
1069 if let Some(frame) = frame {
1070 // Ensure that the handle has been invalidated.
1071 assert!(!frame.is_valid(&mut caller));
1072 // Ensure that attempts to fetch data from the frame
1073 // fail with a clean `Err`, not a panic or crash.
1074 let result = frame.wasm_function_index_and_pc(&mut caller);
1075 assert!(result.is_err());
1076 // Ensure that we can get a new frame handle and use it.
1077 let frame = caller.debug_exit_frames().next().unwrap();
1078 assert_eq!(frame.num_locals(&mut caller)?, 2);
1079 assert_eq!(frame.local(&mut caller, 0)?.unwrap_i32(), 1);
1080 assert_eq!(frame.local(&mut caller, 1)?.unwrap_i32(), 2);
1081 } else {
1082 let frame = caller.debug_exit_frames().next().unwrap();
1083 assert_eq!(frame.num_locals(&mut caller)?, 2);
1084 assert_eq!(frame.local(&mut caller, 0)?.unwrap_i32(), 3);
1085 assert_eq!(frame.local(&mut caller, 1)?.unwrap_i32(), 4);
1086 *handle.lock().unwrap() = Some(frame);
1087 }
1088 Ok(())
1089 })
1090 });
1091
1092 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(hostfunc)]).await?;
1093 let main = instance.get_func(&mut store, "main").unwrap();
1094 main.call_async(&mut store, &[], &mut []).await?;
1095
1096 Ok(())
1097 }
1098
1099 #[tokio::test]
1100 #[cfg_attr(miri, ignore)]
invalidated_frame_handles_in_dropped_future() -> wasmtime::Result<()>1101 async fn invalidated_frame_handles_in_dropped_future() -> wasmtime::Result<()> {
1102 let (module, mut store) = get_module_and_store(
1103 |_config| {},
1104 r#"
1105 (module
1106 (import "" "" (func))
1107 (func (export "main")
1108 call 0))
1109 "#,
1110 )?;
1111
1112 let handle: Arc<Mutex<Option<FrameHandle>>> = Arc::new(Mutex::new(None));
1113 let handle_clone = handle.clone();
1114
1115 let hostfunc = Func::wrap_async(&mut store, move |mut caller, _args: ()| {
1116 let handle_clone = handle_clone.clone();
1117 Box::new(async move {
1118 let frame = caller.debug_exit_frames().next().unwrap();
1119 *handle_clone.lock().unwrap() = Some(frame);
1120 tokio::task::yield_now().await;
1121 })
1122 });
1123
1124 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(hostfunc)]).await?;
1125 let main = instance.get_func(&mut store, "main").unwrap();
1126 let future = Box::pin(main.call_async(&mut store, &[], &mut []));
1127
1128 // Poll once, then drop.
1129 let poll_once = PollOnce::new(future);
1130 let future = poll_once.await;
1131
1132 drop(future);
1133
1134 // The frame handle should now be invalid.
1135 let mut handle = handle.lock().unwrap();
1136 let frame = handle.take().unwrap();
1137 assert!(!frame.is_valid(&mut store));
1138
1139 Ok(())
1140 }
1141
1142 #[test]
1143 #[cfg_attr(miri, ignore)]
module_bytecode() -> wasmtime::Result<()>1144 fn module_bytecode() -> wasmtime::Result<()> {
1145 let wasm = wat::parse_str(
1146 r#"
1147 (module
1148 (func (export "add") (param i32 i32) (result i32)
1149 local.get 0
1150 local.get 1
1151 i32.add
1152 )
1153 )
1154 "#,
1155 )
1156 .unwrap();
1157
1158 let mut config = Config::default();
1159 config.guest_debug(true);
1160 let engine = Engine::new(&config)?;
1161 let module = Module::new(&engine, &wasm)?;
1162
1163 assert_eq!(module.debug_bytecode(), Some(&wasm[..]));
1164
1165 Ok(())
1166 }
1167
1168 #[test]
1169 #[cfg_attr(miri, ignore)]
module_bytecode_absent_without_debug() -> wasmtime::Result<()>1170 fn module_bytecode_absent_without_debug() -> wasmtime::Result<()> {
1171 let wasm = wat::parse_str("(module)").unwrap();
1172
1173 let mut config = Config::default();
1174 config.guest_debug(false);
1175 let engine = Engine::new(&config)?;
1176 let module = Module::new(&engine, &wasm)?;
1177
1178 assert_eq!(module.debug_bytecode(), None);
1179
1180 Ok(())
1181 }
1182
1183 #[test]
1184 #[cfg_attr(miri, ignore)]
component_bytecode() -> wasmtime::Result<()>1185 fn component_bytecode() -> wasmtime::Result<()> {
1186 use wasmtime::component::{Component, Linker};
1187
1188 // Build the bytecode for each core module by compiling them
1189 // standalone.
1190 let m1_body = r#"(func (export "f1") (result i32) i32.const 42)"#;
1191 let m2_body = r#"(func (export "f2") (result i32) i32.const 99)"#;
1192 let m1_wasm = wat::parse_str(&format!("(module $m1 {m1_body})")).unwrap();
1193 let m2_wasm = wat::parse_str(&format!("(module $m2 {m2_body})")).unwrap();
1194
1195 // Build a component that embeds both core modules inline.
1196 let component_wasm = wat::parse_str(&format!(
1197 r#"(component
1198 (core module $m1 {m1_body})
1199 (core instance $i1 (instantiate (module $m1)))
1200 (core module $m2 {m2_body})
1201 (core instance $i2 (instantiate (module $m2))))
1202 "#,
1203 ))
1204 .unwrap();
1205
1206 let mut config = Config::default();
1207 config.guest_debug(true);
1208 let engine = Engine::new(&config)?;
1209
1210 let component = Component::new(&engine, &component_wasm)?;
1211 let linker: Linker<()> = Linker::new(&engine);
1212 let mut store = Store::new(&engine, ());
1213 linker.instantiate(&mut store, &component)?;
1214
1215 let modules = store.debug_all_modules();
1216 assert_eq!(modules.len(), 2);
1217
1218 // Modules should be registered in offset order. The API doesn't
1219 // guarantee this, but this suffices for a test.
1220 assert_eq!(modules[0].debug_bytecode().unwrap(), &m1_wasm[..]);
1221 assert_eq!(modules[1].debug_bytecode().unwrap(), &m2_wasm[..]);
1222
1223 Ok(())
1224 }
1225
1226 #[test]
1227 #[cfg_attr(miri, ignore)]
debug_ids() -> wasmtime::Result<()>1228 fn debug_ids() -> wasmtime::Result<()> {
1229 let mut config = Config::default();
1230 config.guest_debug(true);
1231 config.wasm_exceptions(true);
1232 let engine = Engine::new(&config)?;
1233 let mut store = Store::new(&engine, ());
1234 let module1 = Module::new(
1235 &engine,
1236 r#"
1237 (module
1238 (memory 1 1)
1239 (memory 1 1)
1240 (global (mut i32) (i32.const 0))
1241 (global (mut i32) (i32.const 1))
1242 (table 1 1 funcref)
1243 (table 1 1 funcref)
1244 (tag (param i32))
1245 (tag (param i64)))
1246 "#,
1247 )?;
1248
1249 let module2 = Module::new(
1250 &engine,
1251 r#"
1252 (module
1253 (memory (export "m") 1 1))
1254 "#,
1255 )?;
1256
1257 let instance1 = Instance::new(&mut store, &module1, &[])?;
1258 let instance2 = Instance::new(&mut store, &module2, &[])?;
1259 let instance3 = Instance::new(&mut store, &module1, &[])?;
1260
1261 assert_ne!(
1262 module1.debug_index_in_engine(),
1263 module2.debug_index_in_engine()
1264 );
1265 assert_ne!(
1266 instance1.debug_index_in_store(),
1267 instance2.debug_index_in_store()
1268 );
1269 assert_ne!(
1270 instance1
1271 .debug_memory(&mut store, 0)
1272 .unwrap()
1273 .debug_index_in_store(),
1274 instance1
1275 .debug_memory(&mut store, 1)
1276 .unwrap()
1277 .debug_index_in_store()
1278 );
1279 assert_ne!(
1280 instance1
1281 .debug_memory(&mut store, 0)
1282 .unwrap()
1283 .debug_index_in_store(),
1284 instance2
1285 .debug_memory(&mut store, 0)
1286 .unwrap()
1287 .debug_index_in_store()
1288 );
1289 assert_ne!(
1290 instance1
1291 .debug_memory(&mut store, 0)
1292 .unwrap()
1293 .debug_index_in_store(),
1294 instance3
1295 .debug_memory(&mut store, 0)
1296 .unwrap()
1297 .debug_index_in_store()
1298 );
1299 assert_ne!(
1300 instance1
1301 .debug_global(&mut store, 0)
1302 .unwrap()
1303 .debug_index_in_store(),
1304 instance3
1305 .debug_global(&mut store, 0)
1306 .unwrap()
1307 .debug_index_in_store()
1308 );
1309 assert_ne!(
1310 instance1
1311 .debug_table(&mut store, 0)
1312 .unwrap()
1313 .debug_index_in_store(),
1314 instance3
1315 .debug_table(&mut store, 0)
1316 .unwrap()
1317 .debug_index_in_store()
1318 );
1319 assert_ne!(
1320 instance1
1321 .debug_tag(&mut store, 0)
1322 .unwrap()
1323 .debug_index_in_store(),
1324 instance3
1325 .debug_tag(&mut store, 0)
1326 .unwrap()
1327 .debug_index_in_store()
1328 );
1329
1330 let m_via_export = instance2
1331 .get_export(&mut store, "m")
1332 .unwrap()
1333 .into_memory()
1334 .unwrap();
1335 let m_via_introspection = instance2.debug_memory(&mut store, 0).unwrap();
1336 assert_eq!(
1337 m_via_export.debug_index_in_store(),
1338 m_via_introspection.debug_index_in_store()
1339 );
1340
1341 Ok(())
1342 }
1343
1344 #[tokio::test]
1345 #[cfg_attr(miri, ignore)]
single_step_before_instantiation() -> wasmtime::Result<()>1346 async fn single_step_before_instantiation() -> wasmtime::Result<()> {
1347 let _ = env_logger::try_init();
1348
1349 let mut config = Config::default();
1350 config.guest_debug(true);
1351 let engine = Engine::new(&config)?;
1352 let module = Module::new(
1353 &engine,
1354 r#"
1355 (module
1356 (func (export "main") (param i32 i32) (result i32)
1357 local.get 0
1358 local.get 1
1359 i32.add))
1360 "#,
1361 )?;
1362 let mut store = Store::new(&engine, ());
1363
1364 // Enable single-stepping *before* instantiation. The module has not
1365 // been registered with this store yet.
1366 store.edit_breakpoints().unwrap().single_step(true).unwrap();
1367 assert!(store.is_single_step());
1368
1369 #[derive(Clone)]
1370 struct CountingHandler(Arc<AtomicUsize>);
1371 impl DebugHandler for CountingHandler {
1372 type Data = ();
1373 async fn handle(&self, _store: StoreContextMut<'_, ()>, event: DebugEvent<'_>) {
1374 match event {
1375 DebugEvent::Breakpoint => {
1376 self.0.fetch_add(1, Ordering::Relaxed);
1377 }
1378 _ => {}
1379 }
1380 }
1381 }
1382
1383 let counter = Arc::new(AtomicUsize::new(0));
1384 store.set_debug_handler(CountingHandler(counter.clone()));
1385
1386 let instance = Instance::new_async(&mut store, &module, &[]).await?;
1387 let func = instance.get_func(&mut store, "main").unwrap();
1388 let mut results = [Val::I32(0)];
1389 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
1390 .await?;
1391 assert_eq!(results[0].unwrap_i32(), 3);
1392
1393 assert_eq!(counter.load(Ordering::Relaxed), 4);
1394
1395 Ok(())
1396 }
1397
1398 #[tokio::test]
1399 #[cfg_attr(miri, ignore)]
early_epoch_yield_still_has_vmctx() -> wasmtime::Result<()>1400 async fn early_epoch_yield_still_has_vmctx() -> wasmtime::Result<()> {
1401 let _ = env_logger::try_init();
1402
1403 let mut config = Config::default();
1404 config.guest_debug(true);
1405 config.epoch_interruption(true);
1406 let engine = Engine::new(&config)?;
1407 let module = Module::new(
1408 &engine,
1409 r#"
1410 (module
1411 (func (export "main") (param i32 i32) (result i32)
1412 local.get 0
1413 local.get 1
1414 i32.add))
1415 "#,
1416 )?;
1417 let mut store = Store::new(&engine, ());
1418 store.set_epoch_deadline(1);
1419 store.epoch_deadline_async_yield_and_update(1);
1420 engine.increment_epoch();
1421
1422 #[derive(Clone)]
1423 struct H;
1424 impl DebugHandler for H {
1425 type Data = ();
1426 async fn handle(&self, mut store: StoreContextMut<'_, ()>, _event: DebugEvent<'_>) {
1427 // Ensure we can access the instance (which accesses the
1428 // vmctx slot in the frame's debug info).
1429 let frame = store.debug_exit_frames().next().unwrap();
1430 let _instance = frame.instance(&mut store);
1431 }
1432 }
1433
1434 store.set_debug_handler(H);
1435
1436 let instance = Instance::new_async(&mut store, &module, &[]).await?;
1437 let func = instance.get_func(&mut store, "main").unwrap();
1438 let mut results = [Val::I32(0)];
1439 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
1440 .await?;
1441 assert_eq!(results[0].unwrap_i32(), 3);
1442
1443 Ok(())
1444 }
1445
1446 #[tokio::test]
1447 #[cfg_attr(miri, ignore)]
breakpoint_slips_to_first_opcode() -> wasmtime::Result<()>1448 async fn breakpoint_slips_to_first_opcode() -> wasmtime::Result<()> {
1449 let _ = env_logger::try_init();
1450
1451 // Breakpoints set at the function body start (which includes the
1452 // local declarations and precedes the first opcode) should be
1453 // "slipped" forward to the first opcode. This matches how LLDB
1454 // sets breakpoints using DWARF `DW_AT_low_pc`.
1455 //
1456 // For the WAT below, `wasm-objdump -d` shows:
1457 //
1458 // ```
1459 // 000023 func[0] <main>:
1460 // 000024: 20 00 | local.get 0
1461 // 000026: 20 01 | local.get 1
1462 // 000028: 6a | i32.add
1463 // 000029: 0b | end
1464 // ```
1465 //
1466 // 0x23 is the function body start (locals count byte), while 0x24
1467 // is the first opcode. Setting a breakpoint at 0x23 should slip
1468 // to 0x24.
1469 let (module, mut store) = get_module_and_store(
1470 |_config| {},
1471 r#"
1472 (module
1473 (func (export "main") (param i32 i32) (result i32)
1474 local.get 0
1475 local.get 1
1476 i32.add))
1477 "#,
1478 )?;
1479
1480 debug_event_checker!(
1481 D, store,
1482 { 0 ;
1483 wasmtime::DebugEvent::Breakpoint => {
1484 let stack = store.debug_exit_frames().next().unwrap();
1485 let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap();
1486 assert_eq!(func.as_u32(), 0);
1487 // The breakpoint should fire at the first opcode
1488 // (0x24), not at the function body start (0x23).
1489 assert_eq!(pc.raw(), 0x24);
1490 }
1491 }
1492 );
1493
1494 let (handler, counter) = D::new_and_counter();
1495 store.set_debug_handler(handler);
1496
1497 store
1498 .edit_breakpoints()
1499 .unwrap()
1500 .add_breakpoint(&module, ModulePC::new(0x23))?;
1501
1502 let instance = Instance::new_async(&mut store, &module, &[]).await?;
1503 let func = instance.get_func(&mut store, "main").unwrap();
1504 let mut results = [Val::I32(0)];
1505 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
1506 .await?;
1507 assert_eq!(counter.load(Ordering::Relaxed), 1);
1508 assert_eq!(results[0].unwrap_i32(), 3);
1509
1510 // The actual breakpoint stored should be at the slipped PC.
1511 let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>();
1512 assert_eq!(breakpoints.len(), 1);
1513 assert_eq!(breakpoints[0].pc, ModulePC::new(0x24));
1514
1515 // Removing with the originally requested PC should work.
1516 store
1517 .edit_breakpoints()
1518 .unwrap()
1519 .remove_breakpoint(&module, ModulePC::new(0x23))?;
1520 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results)
1521 .await?;
1522 // Counter should not have incremented now that we removed the
1523 // breakpoint.
1524 assert_eq!(counter.load(Ordering::Relaxed), 1);
1525
1526 Ok(())
1527 }
1528
1529 #[tokio::test]
1530 #[cfg_attr(miri, ignore)]
component_module_relative_breakpoint_pcs() -> wasmtime::Result<()>1531 async fn component_module_relative_breakpoint_pcs() -> wasmtime::Result<()> {
1532 use wasmtime::component::{Component, Linker};
1533
1534 let _ = env_logger::try_init();
1535
1536 let m1_body = r#"(func (export "f1") (param i32 i32) (result i32)
1537 local.get 0
1538 local.get 1
1539 i32.add)"#;
1540 let m2_body = r#"(func (export "f2") (param i32 i32) (result i32)
1541 local.get 0
1542 local.get 1
1543 i32.mul)"#;
1544
1545 let _m1_wasm = wat::parse_str(&format!("(module {m1_body})"))?;
1546 let _m2_wasm = wat::parse_str(&format!("(module {m2_body})"))?;
1547
1548 let component_wat = format!(
1549 r#"(component
1550 (core module $m1 {m1_body})
1551 (core instance $i1 (instantiate $m1))
1552 (core module $m2 {m2_body})
1553 (core instance $i2 (instantiate $m2))
1554 (func (export "f1") (param "a" s32) (param "b" s32) (result s32)
1555 (canon lift (core func $i1 "f1")))
1556 (func (export "f2") (param "a" s32) (param "b" s32) (result s32)
1557 (canon lift (core func $i2 "f2"))))"#,
1558 );
1559
1560 let mut config = Config::default();
1561 config.guest_debug(true);
1562 let engine = Engine::new(&config)?;
1563
1564 let component = Component::new(&engine, &component_wat)?;
1565 let linker: Linker<()> = Linker::new(&engine);
1566 let mut store = Store::new(&engine, ());
1567
1568 let instance = linker.instantiate_async(&mut store, &component).await?;
1569
1570 let modules = store.debug_all_modules();
1571 assert_eq!(modules.len(), 2);
1572
1573 // The i32.add / i32.mul instruction is at module-relative offset
1574 // 0x26 in both modules.
1575 let breakpoint_pc = ModulePC::new(0x26);
1576
1577 // Record breakpoint PCs seen in each event.
1578 let observed_pcs = Arc::new(Mutex::new(Vec::<(u32, u32)>::new()));
1579 let observed_pcs_clone = observed_pcs.clone();
1580
1581 #[derive(Clone)]
1582 struct D(Arc<Mutex<Vec<(u32, u32)>>>);
1583 impl DebugHandler for D {
1584 type Data = ();
1585 fn handle(
1586 &self,
1587 mut store: StoreContextMut<'_, ()>,
1588 _event: DebugEvent<'_>,
1589 ) -> impl std::future::Future<Output = ()> + Send {
1590 let frame = store.debug_exit_frames().next().unwrap();
1591 let (func, pc) = frame
1592 .wasm_function_index_and_pc(&mut store)
1593 .unwrap()
1594 .unwrap();
1595 self.0.lock().unwrap().push((func.as_u32(), pc.raw()));
1596 async {}
1597 }
1598 }
1599 store.set_debug_handler(D(observed_pcs_clone));
1600
1601 // Set breakpoints at the same module-relative PC (0x26) in both
1602 // modules.
1603 store
1604 .edit_breakpoints()
1605 .unwrap()
1606 .add_breakpoint(&modules[0], breakpoint_pc)?;
1607 store
1608 .edit_breakpoints()
1609 .unwrap()
1610 .add_breakpoint(&modules[1], breakpoint_pc)?;
1611
1612 let f1 = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "f1")?;
1613 let (result,) = f1.call_async(&mut store, (3, 5)).await?;
1614 assert_eq!(result, 8);
1615
1616 let f2 = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "f2")?;
1617 let (result,) = f2.call_async(&mut store, (3, 5)).await?;
1618 assert_eq!(result, 15);
1619
1620 // Both breakpoint PCs should be 0x26 (module-relative).
1621 let pcs = observed_pcs.lock().unwrap();
1622 assert_eq!(pcs.len(), 2);
1623 assert_eq!(pcs[0], (0, 0x26));
1624 assert_eq!(pcs[1], (0, 0x26));
1625
1626 Ok(())
1627 }
1628