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] 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] 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 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 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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. 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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)] 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