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).unwrap(); 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.wasm_exceptions(true); 506 }, 507 r#" 508 (module 509 (tag $t (param i32)) 510 (func (export "main") 511 call 1) 512 (func 513 (local $i i32) 514 (local.set $i (i32.const 100)) 515 (throw $t (i32.const 42)))) 516 "#, 517 )?; 518 519 debug_event_checker!( 520 D, store, 521 { 0 ; 522 wasmtime::DebugEvent::UncaughtExceptionThrown(e) => { 523 assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42); 524 let mut stack = store.debug_frames().expect("frame cursor must be available"); 525 assert!(!stack.done()); 526 assert_eq!(stack.num_locals(), 1); 527 assert_eq!(stack.local(0).unwrap_i32(), 100); 528 assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation); 529 assert!(!stack.done()); 530 assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation); 531 assert!(stack.done()); 532 } 533 } 534 ); 535 536 let (handler, counter) = D::new_and_counter(); 537 store.set_debug_handler(handler); 538 539 let instance = Instance::new_async(&mut store, &module, &[]).await?; 540 let func = instance.get_func(&mut store, "main").unwrap(); 541 let mut results = []; 542 let result = func.call_async(&mut store, &[], &mut results).await; 543 assert!(result.is_err()); // Uncaught exception. 544 assert_eq!(counter.load(Ordering::Relaxed), 1); 545 546 Ok(()) 547 } 548 549 #[tokio::test] 550 #[cfg_attr(miri, ignore)] 551 async fn caught_exception_events() -> wasmtime::Result<()> { 552 let _ = env_logger::try_init(); 553 554 let (module, mut store) = get_module_and_store( 555 |config| { 556 config.wasm_exceptions(true); 557 }, 558 r#" 559 (module 560 (tag $t (param i32)) 561 (func (export "main") 562 (block $b (result i32) 563 (try_table (catch $t $b) 564 call 1) 565 i32.const 0) 566 drop) 567 (func 568 (local $i i32) 569 (local.set $i (i32.const 100)) 570 (throw $t (i32.const 42)))) 571 "#, 572 )?; 573 574 debug_event_checker!( 575 D, store, 576 { 0 ; 577 wasmtime::DebugEvent::CaughtExceptionThrown(e) => { 578 assert_eq!(e.field(&mut store, 0).unwrap().unwrap_i32(), 42); 579 let mut stack = store.debug_frames().expect("frame cursor must be available"); 580 assert!(!stack.done()); 581 assert_eq!(stack.num_locals(), 1); 582 assert_eq!(stack.local(0).unwrap_i32(), 100); 583 assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation); 584 assert!(!stack.done()); 585 assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation); 586 assert!(stack.done()); 587 } 588 } 589 ); 590 591 let (handler, counter) = D::new_and_counter(); 592 store.set_debug_handler(handler); 593 594 let instance = Instance::new_async(&mut store, &module, &[]).await?; 595 let func = instance.get_func(&mut store, "main").unwrap(); 596 let mut results = []; 597 func.call_async(&mut store, &[], &mut results).await?; 598 assert_eq!(counter.load(Ordering::Relaxed), 1); 599 600 Ok(()) 601 } 602 603 #[tokio::test] 604 #[cfg_attr(miri, ignore)] 605 async fn hostcall_trap_events() -> wasmtime::Result<()> { 606 let _ = env_logger::try_init(); 607 608 let (module, mut store) = get_module_and_store( 609 |config| { 610 config.wasm_exceptions(true); 611 }, 612 r#" 613 (module 614 (func (export "main") 615 i32.const 0 616 i32.const 0 617 i32.div_u 618 drop)) 619 "#, 620 )?; 621 622 debug_event_checker!( 623 D, store, 624 { 0 ; 625 wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => {} 626 } 627 ); 628 629 let (handler, counter) = D::new_and_counter(); 630 store.set_debug_handler(handler); 631 632 let instance = Instance::new_async(&mut store, &module, &[]).await?; 633 let func = instance.get_func(&mut store, "main").unwrap(); 634 let mut results = []; 635 let result = func.call_async(&mut store, &[], &mut results).await; 636 assert!(result.is_err()); // Uncaught trap. 637 assert_eq!(counter.load(Ordering::Relaxed), 1); 638 639 Ok(()) 640 } 641 642 #[tokio::test] 643 #[cfg_attr(miri, ignore)] 644 async fn hostcall_error_events() -> wasmtime::Result<()> { 645 let _ = env_logger::try_init(); 646 647 let (module, mut store) = get_module_and_store( 648 |config| { 649 config.wasm_exceptions(true); 650 }, 651 r#" 652 (module 653 (import "" "do_a_trap" (func)) 654 (func (export "main") 655 call 0)) 656 "#, 657 )?; 658 659 debug_event_checker!( 660 D, store, 661 { 0 ; 662 wasmtime::DebugEvent::HostcallError(e) => { 663 assert!(format!("{e:?}").contains("secret error message")); 664 } 665 } 666 ); 667 668 let (handler, counter) = D::new_and_counter(); 669 store.set_debug_handler(handler); 670 671 let do_a_trap = Func::wrap( 672 &mut store, 673 |_caller: Caller<'_, ()>| -> wasmtime::Result<()> { 674 Err(wasmtime::format_err!("secret error message")) 675 }, 676 ); 677 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(do_a_trap)]).await?; 678 let func = instance.get_func(&mut store, "main").unwrap(); 679 let mut results = []; 680 let result = func.call_async(&mut store, &[], &mut results).await; 681 assert!(result.is_err()); // Uncaught trap. 682 assert_eq!(counter.load(Ordering::Relaxed), 1); 683 Ok(()) 684 } 685 686 #[tokio::test] 687 #[cfg_attr(miri, ignore)] 688 async fn breakpoint_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") (param i32 i32) (result i32) 698 local.get 0 699 local.get 1 700 i32.add)) 701 "#, 702 )?; 703 704 debug_event_checker!( 705 D, store, 706 { 0 ; 707 wasmtime::DebugEvent::Breakpoint => { 708 let mut stack = store.debug_frames().expect("frame cursor must be available"); 709 assert!(!stack.done()); 710 assert_eq!(stack.num_locals(), 2); 711 assert_eq!(stack.local(0).unwrap_i32(), 1); 712 assert_eq!(stack.local(1).unwrap_i32(), 2); 713 let (func, pc) = stack.wasm_function_index_and_pc().unwrap(); 714 assert_eq!(func.as_u32(), 0); 715 assert_eq!(pc, 0x28); 716 assert_eq!(stack.move_to_parent(), FrameParentResult::SameActivation); 717 assert!(stack.done()); 718 } 719 } 720 ); 721 722 let (handler, counter) = D::new_and_counter(); 723 store.set_debug_handler(handler); 724 store 725 .edit_breakpoints() 726 .unwrap() 727 .add_breakpoint(&module, 0x28)?; 728 729 let instance = Instance::new_async(&mut store, &module, &[]).await?; 730 let func = instance.get_func(&mut store, "main").unwrap(); 731 let mut results = [Val::I32(0)]; 732 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 733 .await?; 734 assert_eq!(counter.load(Ordering::Relaxed), 1); 735 assert_eq!(results[0].unwrap_i32(), 3); 736 737 let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>(); 738 assert_eq!(breakpoints.len(), 1); 739 assert!(Module::same(&breakpoints[0].module, &module)); 740 assert_eq!(breakpoints[0].pc, 0x28); 741 742 store 743 .edit_breakpoints() 744 .unwrap() 745 .remove_breakpoint(&module, 0x28)?; 746 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 747 .await?; 748 assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above. 749 assert_eq!(results[0].unwrap_i32(), 3); 750 751 // Enable single-step mode (on top of the breakpoint already enabled). 752 assert!(!store.is_single_step()); 753 store.edit_breakpoints().unwrap().single_step(true).unwrap(); 754 assert!(store.is_single_step()); 755 756 debug_event_checker!( 757 D2, store, 758 { 0 ; 759 wasmtime::DebugEvent::Breakpoint => { 760 let stack = store.debug_frames().unwrap(); 761 assert!(!stack.done()); 762 let (_, pc) = stack.wasm_function_index_and_pc().unwrap(); 763 assert_eq!(pc, 0x24); 764 } 765 }, 766 { 767 1 ; 768 wasmtime::DebugEvent::Breakpoint => { 769 let stack = store.debug_frames().unwrap(); 770 assert!(!stack.done()); 771 let (_, pc) = stack.wasm_function_index_and_pc().unwrap(); 772 assert_eq!(pc, 0x26); 773 } 774 }, 775 { 776 2 ; 777 wasmtime::DebugEvent::Breakpoint => { 778 let stack = store.debug_frames().unwrap(); 779 assert!(!stack.done()); 780 let (_, pc) = stack.wasm_function_index_and_pc().unwrap(); 781 assert_eq!(pc, 0x28); 782 } 783 }, 784 { 785 3 ; 786 wasmtime::DebugEvent::Breakpoint => { 787 let stack = store.debug_frames().unwrap(); 788 assert!(!stack.done()); 789 let (_, pc) = stack.wasm_function_index_and_pc().unwrap(); 790 assert_eq!(pc, 0x29); 791 } 792 } 793 ); 794 795 let (handler, counter) = D2::new_and_counter(); 796 store.set_debug_handler(handler); 797 798 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 799 .await?; 800 assert_eq!(counter.load(Ordering::Relaxed), 4); 801 802 // Re-enable individual breakpoint. 803 store 804 .edit_breakpoints() 805 .unwrap() 806 .add_breakpoint(&module, 0x28) 807 .unwrap(); 808 809 // Now disable single-stepping. The single breakpoint set above 810 // should still remain. 811 store 812 .edit_breakpoints() 813 .unwrap() 814 .single_step(false) 815 .unwrap(); 816 817 let (handler, counter) = D::new_and_counter(); 818 store.set_debug_handler(handler); 819 820 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 821 .await?; 822 assert_eq!(counter.load(Ordering::Relaxed), 1); 823 824 Ok(()) 825 } 826 827 #[tokio::test] 828 #[cfg_attr(miri, ignore)] 829 async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> { 830 let _ = env_logger::try_init(); 831 832 let (module, mut store) = get_module_and_store( 833 |config| { 834 config.wasm_exceptions(true); 835 config.compiler_inlining(true); 836 unsafe { 837 config.cranelift_flag_set("wasmtime_inlining_intra_module", "true"); 838 } 839 }, 840 r#" 841 (module 842 (func $f (export "f") (param i32 i32) (result i32) 843 local.get 0 844 local.get 1 845 i32.add) 846 847 (func (export "main") (param i32 i32) (result i32) 848 local.get 0 849 local.get 1 850 call $f)) 851 "#, 852 )?; 853 854 debug_event_checker!( 855 D, store, 856 { 0 ; 857 wasmtime::DebugEvent::Breakpoint => {} 858 }, 859 { 1 ; 860 wasmtime::DebugEvent::Breakpoint => {} 861 } 862 ); 863 864 let (handler, counter) = D::new_and_counter(); 865 store.set_debug_handler(handler); 866 store 867 .edit_breakpoints() 868 .unwrap() 869 .add_breakpoint(&module, 0x2d)?; // `i32.add` in `$f`. 870 871 let instance = Instance::new_async(&mut store, &module, &[]).await?; 872 let func_main = instance.get_func(&mut store, "main").unwrap(); 873 let func_f = instance.get_func(&mut store, "f").unwrap(); 874 let mut results = [Val::I32(0)]; 875 // Breakpoint in `$f` should have been hit in `main` even if it 876 // was inlined. 877 func_main 878 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 879 .await?; 880 assert_eq!(counter.load(Ordering::Relaxed), 1); 881 assert_eq!(results[0].unwrap_i32(), 3); 882 883 // Breakpoint in `$f` should be hit when called directly, too. 884 func_f 885 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 886 .await?; 887 assert_eq!(counter.load(Ordering::Relaxed), 2); 888 assert_eq!(results[0].unwrap_i32(), 3); 889 890 Ok(()) 891 } 892 893 #[tokio::test] 894 #[cfg_attr(miri, ignore)] 895 async fn epoch_events() -> wasmtime::Result<()> { 896 let _ = env_logger::try_init(); 897 898 let (module, mut store) = get_module_and_store( 899 |config| { 900 config.epoch_interruption(true); 901 }, 902 r#" 903 (module 904 (func $f (export "f") (param i32 i32) (result i32) 905 local.get 0 906 local.get 1 907 i32.add)) 908 "#, 909 )?; 910 911 debug_event_checker!( 912 D, store, 913 { 0 ; 914 wasmtime::DebugEvent::EpochYield => {} 915 } 916 ); 917 918 let (handler, counter) = D::new_and_counter(); 919 store.set_debug_handler(handler); 920 921 store.set_epoch_deadline(1); 922 store.epoch_deadline_async_yield_and_update(1); 923 store.engine().increment_epoch(); 924 925 let instance = Instance::new_async(&mut store, &module, &[]).await?; 926 let func_f = instance.get_func(&mut store, "f").unwrap(); 927 let mut results = [Val::I32(0)]; 928 func_f 929 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 930 .await?; 931 assert_eq!(counter.load(Ordering::Relaxed), 1); 932 assert_eq!(results[0].unwrap_i32(), 3); 933 934 Ok(()) 935 } 936