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 macro_rules! debug_event_checker { 503 ($ty:tt, 504 $store:tt, 505 $( 506 { $i:expr ; $pat:pat => $body:tt } 507 ),*) 508 => 509 { 510 #[derive(Clone)] 511 struct $ty(Arc<AtomicUsize>); 512 impl $ty { 513 fn new_and_counter() -> (Self, Arc<AtomicUsize>) { 514 let counter = Arc::new(AtomicUsize::new(0)); 515 let counter_clone = counter.clone(); 516 ($ty(counter), counter_clone) 517 } 518 } 519 impl DebugHandler for $ty { 520 type Data = (); 521 fn handle( 522 &self, 523 #[allow(unused_variables, reason = "macro rules")] 524 #[allow(unused_mut, reason = "macro rules")] 525 mut $store: StoreContextMut<'_, ()>, 526 event: DebugEvent<'_>, 527 ) -> impl Future<Output = ()> + Send { 528 let step = self.0.fetch_add(1, Ordering::Relaxed); 529 async move { 530 if false {} 531 $( 532 else if step == $i { 533 match event { 534 $pat => { 535 $body; 536 } 537 _ => panic!("Incorrect event"), 538 } 539 } 540 )* 541 else { 542 panic!("Too many steps"); 543 } 544 } 545 } 546 } 547 } 548 } 549 550 #[tokio::test] 551 #[cfg_attr(miri, ignore)] 552 async fn uncaught_exception_events() -> wasmtime::Result<()> { 553 let _ = env_logger::try_init(); 554 555 let (module, mut store) = get_module_and_store( 556 |config| { 557 config.wasm_exceptions(true); 558 }, 559 r#" 560 (module 561 (tag $t (param i32)) 562 (func (export "main") 563 call 1) 564 (func 565 (local $i i32) 566 (local.set $i (i32.const 100)) 567 (throw $t (i32.const 42)))) 568 "#, 569 )?; 570 571 debug_event_checker!( 572 D, store, 573 { 0 ; 574 wasmtime::DebugEvent::UncaughtExceptionThrown(e) => { 575 assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42); 576 let stack = store.debug_exit_frames().next().unwrap(); 577 assert_eq!(stack.num_locals(&mut store).unwrap(), 1); 578 assert_eq!(stack.local(&mut store, 0).unwrap().unwrap_i32(), 100); 579 let stack = stack.parent(&mut store).unwrap().unwrap(); 580 let stack = stack.parent(&mut store).unwrap(); 581 assert!(stack.is_none()); 582 } 583 } 584 ); 585 586 let (handler, counter) = D::new_and_counter(); 587 store.set_debug_handler(handler); 588 589 let instance = Instance::new_async(&mut store, &module, &[]).await?; 590 let func = instance.get_func(&mut store, "main").unwrap(); 591 let mut results = []; 592 let result = func.call_async(&mut store, &[], &mut results).await; 593 assert!(result.is_err()); // Uncaught exception. 594 assert_eq!(counter.load(Ordering::Relaxed), 1); 595 596 Ok(()) 597 } 598 599 #[tokio::test] 600 #[cfg_attr(miri, ignore)] 601 async fn caught_exception_events() -> wasmtime::Result<()> { 602 let _ = env_logger::try_init(); 603 604 let (module, mut store) = get_module_and_store( 605 |config| { 606 config.wasm_exceptions(true); 607 }, 608 r#" 609 (module 610 (tag $t (param i32)) 611 (func (export "main") 612 (block $b (result i32) 613 (try_table (catch $t $b) 614 call 1) 615 i32.const 0) 616 drop) 617 (func 618 (local $i i32) 619 (local.set $i (i32.const 100)) 620 (throw $t (i32.const 42)))) 621 "#, 622 )?; 623 624 debug_event_checker!( 625 D, store, 626 { 0 ; 627 wasmtime::DebugEvent::CaughtExceptionThrown(e) => { 628 assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42); 629 let stack = store.debug_exit_frames().next().unwrap(); 630 assert_eq!(stack.num_locals(&mut store).unwrap(), 1); 631 assert_eq!(stack.local(&mut store, 0).unwrap().unwrap_i32(), 100); 632 let stack = stack.parent(&mut store).unwrap().unwrap(); 633 let stack = stack.parent(&mut store).unwrap(); 634 assert!(stack.is_none()); 635 } 636 } 637 ); 638 639 let (handler, counter) = D::new_and_counter(); 640 store.set_debug_handler(handler); 641 642 let instance = Instance::new_async(&mut store, &module, &[]).await?; 643 let func = instance.get_func(&mut store, "main").unwrap(); 644 let mut results = []; 645 func.call_async(&mut store, &[], &mut results).await?; 646 assert_eq!(counter.load(Ordering::Relaxed), 1); 647 648 Ok(()) 649 } 650 651 #[tokio::test] 652 #[cfg_attr(miri, ignore)] 653 async fn hostcall_trap_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 (func (export "main") 663 i32.const 0 664 i32.const 0 665 i32.div_u 666 drop)) 667 "#, 668 )?; 669 670 debug_event_checker!( 671 D, store, 672 { 0 ; 673 wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => {} 674 } 675 ); 676 677 let (handler, counter) = D::new_and_counter(); 678 store.set_debug_handler(handler); 679 680 let instance = Instance::new_async(&mut store, &module, &[]).await?; 681 let func = instance.get_func(&mut store, "main").unwrap(); 682 let mut results = []; 683 let result = func.call_async(&mut store, &[], &mut results).await; 684 assert!(result.is_err()); // Uncaught trap. 685 assert_eq!(counter.load(Ordering::Relaxed), 1); 686 687 Ok(()) 688 } 689 690 #[tokio::test] 691 #[cfg_attr(miri, ignore)] 692 async fn hostcall_error_events() -> wasmtime::Result<()> { 693 let _ = env_logger::try_init(); 694 695 let (module, mut store) = get_module_and_store( 696 |config| { 697 config.wasm_exceptions(true); 698 }, 699 r#" 700 (module 701 (import "" "do_a_trap" (func)) 702 (func (export "main") 703 call 0)) 704 "#, 705 )?; 706 707 debug_event_checker!( 708 D, store, 709 { 0 ; 710 wasmtime::DebugEvent::HostcallError(e) => { 711 assert!(format!("{e:?}").contains("secret error message")); 712 } 713 } 714 ); 715 716 let (handler, counter) = D::new_and_counter(); 717 store.set_debug_handler(handler); 718 719 let do_a_trap = Func::wrap( 720 &mut store, 721 |_caller: Caller<'_, ()>| -> wasmtime::Result<()> { 722 Err(wasmtime::format_err!("secret error message")) 723 }, 724 ); 725 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(do_a_trap)]).await?; 726 let func = instance.get_func(&mut store, "main").unwrap(); 727 let mut results = []; 728 let result = func.call_async(&mut store, &[], &mut results).await; 729 assert!(result.is_err()); // Uncaught trap. 730 assert_eq!(counter.load(Ordering::Relaxed), 1); 731 Ok(()) 732 } 733 734 #[tokio::test] 735 #[cfg_attr(miri, ignore)] 736 async fn breakpoint_events() -> wasmtime::Result<()> { 737 let _ = env_logger::try_init(); 738 739 let (module, mut store) = get_module_and_store( 740 |config| { 741 config.wasm_exceptions(true); 742 }, 743 r#" 744 (module 745 (func (export "main") (param i32 i32) (result i32) 746 local.get 0 747 local.get 1 748 i32.add)) 749 "#, 750 )?; 751 752 debug_event_checker!( 753 D, store, 754 { 0 ; 755 wasmtime::DebugEvent::Breakpoint => { 756 let stack = store.debug_exit_frames().next().unwrap(); 757 assert_eq!(stack.num_locals(&mut store).unwrap(), 2); 758 assert_eq!(stack.local(&mut store, 0).unwrap().unwrap_i32(), 1); 759 assert_eq!(stack.local(&mut store, 1).unwrap().unwrap_i32(), 2); 760 let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 761 assert_eq!(func.as_u32(), 0); 762 assert_eq!(pc, 0x28); 763 let stack = stack.parent(&mut store).unwrap(); 764 assert!(stack.is_none()); 765 } 766 } 767 ); 768 769 let (handler, counter) = D::new_and_counter(); 770 store.set_debug_handler(handler); 771 store 772 .edit_breakpoints() 773 .unwrap() 774 .add_breakpoint(&module, 0x28)?; 775 776 let instance = Instance::new_async(&mut store, &module, &[]).await?; 777 let func = instance.get_func(&mut store, "main").unwrap(); 778 let mut results = [Val::I32(0)]; 779 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 780 .await?; 781 assert_eq!(counter.load(Ordering::Relaxed), 1); 782 assert_eq!(results[0].unwrap_i32(), 3); 783 784 let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>(); 785 assert_eq!(breakpoints.len(), 1); 786 assert!(Module::same(&breakpoints[0].module, &module)); 787 assert_eq!(breakpoints[0].pc, 0x28); 788 789 store 790 .edit_breakpoints() 791 .unwrap() 792 .remove_breakpoint(&module, 0x28)?; 793 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 794 .await?; 795 assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above. 796 assert_eq!(results[0].unwrap_i32(), 3); 797 798 // Enable single-step mode (on top of the breakpoint already enabled). 799 assert!(!store.is_single_step()); 800 store.edit_breakpoints().unwrap().single_step(true).unwrap(); 801 assert!(store.is_single_step()); 802 803 debug_event_checker!( 804 D2, store, 805 { 0 ; 806 wasmtime::DebugEvent::Breakpoint => { 807 let stack = store.debug_exit_frames().next().unwrap(); 808 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 809 assert_eq!(pc, 0x24); 810 } 811 }, 812 { 813 1 ; 814 wasmtime::DebugEvent::Breakpoint => { 815 let stack = store.debug_exit_frames().next().unwrap(); 816 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 817 assert_eq!(pc, 0x26); 818 } 819 }, 820 { 821 2 ; 822 wasmtime::DebugEvent::Breakpoint => { 823 let stack = store.debug_exit_frames().next().unwrap(); 824 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 825 assert_eq!(pc, 0x28); 826 } 827 }, 828 { 829 3 ; 830 wasmtime::DebugEvent::Breakpoint => { 831 let stack = store.debug_exit_frames().next().unwrap(); 832 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 833 assert_eq!(pc, 0x29); 834 } 835 } 836 ); 837 838 let (handler, counter) = D2::new_and_counter(); 839 store.set_debug_handler(handler); 840 841 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 842 .await?; 843 assert_eq!(counter.load(Ordering::Relaxed), 4); 844 845 // Re-enable individual breakpoint. 846 store 847 .edit_breakpoints() 848 .unwrap() 849 .add_breakpoint(&module, 0x28) 850 .unwrap(); 851 852 // Now disable single-stepping. The single breakpoint set above 853 // should still remain. 854 store 855 .edit_breakpoints() 856 .unwrap() 857 .single_step(false) 858 .unwrap(); 859 860 let (handler, counter) = D::new_and_counter(); 861 store.set_debug_handler(handler); 862 863 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 864 .await?; 865 assert_eq!(counter.load(Ordering::Relaxed), 1); 866 867 Ok(()) 868 } 869 870 #[tokio::test] 871 #[cfg_attr(miri, ignore)] 872 async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> { 873 let _ = env_logger::try_init(); 874 875 let (module, mut store) = get_module_and_store( 876 |config| { 877 config.wasm_exceptions(true); 878 config.compiler_inlining(true); 879 unsafe { 880 config.cranelift_flag_set("wasmtime_inlining_intra_module", "true"); 881 } 882 }, 883 r#" 884 (module 885 (func $f (export "f") (param i32 i32) (result i32) 886 local.get 0 887 local.get 1 888 i32.add) 889 890 (func (export "main") (param i32 i32) (result i32) 891 local.get 0 892 local.get 1 893 call $f)) 894 "#, 895 )?; 896 897 debug_event_checker!( 898 D, store, 899 { 0 ; 900 wasmtime::DebugEvent::Breakpoint => {} 901 }, 902 { 1 ; 903 wasmtime::DebugEvent::Breakpoint => {} 904 } 905 ); 906 907 let (handler, counter) = D::new_and_counter(); 908 store.set_debug_handler(handler); 909 store 910 .edit_breakpoints() 911 .unwrap() 912 .add_breakpoint(&module, 0x2d)?; // `i32.add` in `$f`. 913 914 let instance = Instance::new_async(&mut store, &module, &[]).await?; 915 let func_main = instance.get_func(&mut store, "main").unwrap(); 916 let func_f = instance.get_func(&mut store, "f").unwrap(); 917 let mut results = [Val::I32(0)]; 918 // Breakpoint in `$f` should have been hit in `main` even if it 919 // was inlined. 920 func_main 921 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 922 .await?; 923 assert_eq!(counter.load(Ordering::Relaxed), 1); 924 assert_eq!(results[0].unwrap_i32(), 3); 925 926 // Breakpoint in `$f` should be hit when called directly, too. 927 func_f 928 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 929 .await?; 930 assert_eq!(counter.load(Ordering::Relaxed), 2); 931 assert_eq!(results[0].unwrap_i32(), 3); 932 933 Ok(()) 934 } 935 936 #[tokio::test] 937 #[cfg_attr(miri, ignore)] 938 async fn epoch_events() -> wasmtime::Result<()> { 939 let _ = env_logger::try_init(); 940 941 let (module, mut store) = get_module_and_store( 942 |config| { 943 config.epoch_interruption(true); 944 }, 945 r#" 946 (module 947 (func $f (export "f") (param i32 i32) (result i32) 948 local.get 0 949 local.get 1 950 i32.add)) 951 "#, 952 )?; 953 954 debug_event_checker!( 955 D, store, 956 { 0 ; 957 wasmtime::DebugEvent::EpochYield => {} 958 } 959 ); 960 961 let (handler, counter) = D::new_and_counter(); 962 store.set_debug_handler(handler); 963 964 store.set_epoch_deadline(1); 965 store.epoch_deadline_async_yield_and_update(1); 966 store.engine().increment_epoch(); 967 968 let instance = Instance::new_async(&mut store, &module, &[]).await?; 969 let func_f = instance.get_func(&mut store, "f").unwrap(); 970 let mut results = [Val::I32(0)]; 971 func_f 972 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 973 .await?; 974 assert_eq!(counter.load(Ordering::Relaxed), 1); 975 assert_eq!(results[0].unwrap_i32(), 3); 976 977 Ok(()) 978 } 979 980 #[tokio::test] 981 #[cfg_attr(miri, ignore)] 982 async fn invalidated_frame_handles() -> wasmtime::Result<()> { 983 let (module, mut store) = get_module_and_store( 984 |_config| {}, 985 r#" 986 (module 987 (import "" "" (func)) 988 (func (export "main") 989 (local i32 i32) 990 i32.const 1 991 local.set 0 992 i32.const 2 993 local.set 1 994 call 2 995 call 0) 996 (func 997 (local i32 i32) 998 i32.const 3 999 local.set 0 1000 i32.const 4 1001 local.set 1 1002 call 0)) 1003 "#, 1004 )?; 1005 1006 let handle: Arc<Mutex<Option<FrameHandle>>> = Arc::new(Mutex::new(None)); 1007 1008 let hostfunc = Func::wrap_async(&mut store, move |mut caller, _args: ()| { 1009 let handle = handle.clone(); 1010 Box::new(async move { 1011 let frame = handle.lock().unwrap().take(); 1012 if let Some(frame) = frame { 1013 // Ensure that the handle has been invalidated. 1014 assert!(!frame.is_valid(&mut caller)); 1015 // Ensure that attempts to fetch data from the frame 1016 // fail with a clean `Err`, not a panic or crash. 1017 let result = frame.wasm_function_index_and_pc(&mut caller); 1018 assert!(result.is_err()); 1019 // Ensure that we can get a new frame handle and use it. 1020 let frame = caller.debug_exit_frames().next().unwrap(); 1021 assert_eq!(frame.num_locals(&mut caller)?, 2); 1022 assert_eq!(frame.local(&mut caller, 0)?.unwrap_i32(), 1); 1023 assert_eq!(frame.local(&mut caller, 1)?.unwrap_i32(), 2); 1024 } else { 1025 let frame = caller.debug_exit_frames().next().unwrap(); 1026 assert_eq!(frame.num_locals(&mut caller)?, 2); 1027 assert_eq!(frame.local(&mut caller, 0)?.unwrap_i32(), 3); 1028 assert_eq!(frame.local(&mut caller, 1)?.unwrap_i32(), 4); 1029 *handle.lock().unwrap() = Some(frame); 1030 } 1031 Ok(()) 1032 }) 1033 }); 1034 1035 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(hostfunc)]).await?; 1036 let main = instance.get_func(&mut store, "main").unwrap(); 1037 main.call_async(&mut store, &[], &mut []).await?; 1038 1039 Ok(()) 1040 } 1041 1042 #[tokio::test] 1043 #[cfg_attr(miri, ignore)] 1044 async fn invalidated_frame_handles_in_dropped_future() -> wasmtime::Result<()> { 1045 let (module, mut store) = get_module_and_store( 1046 |_config| {}, 1047 r#" 1048 (module 1049 (import "" "" (func)) 1050 (func (export "main") 1051 call 0)) 1052 "#, 1053 )?; 1054 1055 let handle: Arc<Mutex<Option<FrameHandle>>> = Arc::new(Mutex::new(None)); 1056 let handle_clone = handle.clone(); 1057 1058 let hostfunc = Func::wrap_async(&mut store, move |mut caller, _args: ()| { 1059 let handle_clone = handle_clone.clone(); 1060 Box::new(async move { 1061 let frame = caller.debug_exit_frames().next().unwrap(); 1062 *handle_clone.lock().unwrap() = Some(frame); 1063 tokio::task::yield_now().await; 1064 }) 1065 }); 1066 1067 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(hostfunc)]).await?; 1068 let main = instance.get_func(&mut store, "main").unwrap(); 1069 let future = Box::pin(main.call_async(&mut store, &[], &mut [])); 1070 1071 // Poll once, then drop. 1072 let poll_once = PollOnce::new(future); 1073 let future = poll_once.await; 1074 1075 drop(future); 1076 1077 // The frame handle should now be invalid. 1078 let mut handle = handle.lock().unwrap(); 1079 let frame = handle.take().unwrap(); 1080 assert!(!frame.is_valid(&mut store)); 1081 1082 Ok(()) 1083 } 1084