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