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