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 67 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 57 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 58 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 58 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") (result i32) 698 i32.const 0 699 i32.const 0 700 i32.div_u 701 drop 702 i32.const 42)) 703 "#, 704 )?; 705 706 debug_event_checker!( 707 D, store, 708 { 0 ; 709 wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => { 710 let frame = store.debug_exit_frames().next().unwrap(); 711 let (_func, pc) = frame.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 712 assert_eq!(pc, 0x26); 713 } 714 } 715 ); 716 717 let (handler, counter) = D::new_and_counter(); 718 store.set_debug_handler(handler); 719 720 let instance = Instance::new_async(&mut store, &module, &[]).await?; 721 let func = instance.get_func(&mut store, "main").unwrap(); 722 let mut results = [Val::I32(0)]; 723 let result = func.call_async(&mut store, &[], &mut results).await; 724 assert!(result.is_err()); // Uncaught trap. 725 assert_eq!(counter.load(Ordering::Relaxed), 1); 726 727 Ok(()) 728 } 729 730 #[tokio::test] 731 #[cfg_attr(miri, ignore)] 732 async fn hostcall_error_events() -> wasmtime::Result<()> { 733 let _ = env_logger::try_init(); 734 735 let (module, mut store) = get_module_and_store( 736 |config| { 737 config.wasm_exceptions(true); 738 }, 739 r#" 740 (module 741 (import "" "do_a_trap" (func)) 742 (func (export "main") 743 call 0)) 744 "#, 745 )?; 746 747 debug_event_checker!( 748 D, store, 749 { 0 ; 750 wasmtime::DebugEvent::HostcallError(e) => { 751 assert!(format!("{e:?}").contains("secret error message")); 752 } 753 } 754 ); 755 756 let (handler, counter) = D::new_and_counter(); 757 store.set_debug_handler(handler); 758 759 let do_a_trap = Func::wrap( 760 &mut store, 761 |_caller: Caller<'_, ()>| -> wasmtime::Result<()> { 762 Err(wasmtime::format_err!("secret error message")) 763 }, 764 ); 765 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(do_a_trap)]).await?; 766 let func = instance.get_func(&mut store, "main").unwrap(); 767 let mut results = []; 768 let result = func.call_async(&mut store, &[], &mut results).await; 769 assert!(result.is_err()); // Uncaught trap. 770 assert_eq!(counter.load(Ordering::Relaxed), 1); 771 Ok(()) 772 } 773 774 #[tokio::test] 775 #[cfg_attr(miri, ignore)] 776 async fn breakpoint_events() -> wasmtime::Result<()> { 777 let _ = env_logger::try_init(); 778 779 let (module, mut store) = get_module_and_store( 780 |config| { 781 config.wasm_exceptions(true); 782 }, 783 r#" 784 (module 785 (func (export "main") (param i32 i32) (result i32) 786 local.get 0 787 local.get 1 788 i32.add)) 789 "#, 790 )?; 791 792 debug_event_checker!( 793 D, store, 794 { 0 ; 795 wasmtime::DebugEvent::Breakpoint => { 796 let stack = store.debug_exit_frames().next().unwrap(); 797 assert_eq!(stack.num_locals(&mut store).unwrap(), 2); 798 assert_eq!(stack.local(&mut store, 0).unwrap().unwrap_i32(), 1); 799 assert_eq!(stack.local(&mut store, 1).unwrap().unwrap_i32(), 2); 800 let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 801 assert_eq!(func.as_u32(), 0); 802 assert_eq!(pc, 0x28); 803 let stack = stack.parent(&mut store).unwrap(); 804 assert!(stack.is_none()); 805 } 806 } 807 ); 808 809 let (handler, counter) = D::new_and_counter(); 810 store.set_debug_handler(handler); 811 store 812 .edit_breakpoints() 813 .unwrap() 814 .add_breakpoint(&module, 0x28)?; 815 816 let instance = Instance::new_async(&mut store, &module, &[]).await?; 817 let func = instance.get_func(&mut store, "main").unwrap(); 818 let mut results = [Val::I32(0)]; 819 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 820 .await?; 821 assert_eq!(counter.load(Ordering::Relaxed), 1); 822 assert_eq!(results[0].unwrap_i32(), 3); 823 824 let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>(); 825 assert_eq!(breakpoints.len(), 1); 826 assert!(Module::same(&breakpoints[0].module, &module)); 827 assert_eq!(breakpoints[0].pc, 0x28); 828 829 store 830 .edit_breakpoints() 831 .unwrap() 832 .remove_breakpoint(&module, 0x28)?; 833 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 834 .await?; 835 assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above. 836 assert_eq!(results[0].unwrap_i32(), 3); 837 838 // Enable single-step mode (on top of the breakpoint already enabled). 839 assert!(!store.is_single_step()); 840 store.edit_breakpoints().unwrap().single_step(true).unwrap(); 841 assert!(store.is_single_step()); 842 843 debug_event_checker!( 844 D2, store, 845 { 0 ; 846 wasmtime::DebugEvent::Breakpoint => { 847 let stack = store.debug_exit_frames().next().unwrap(); 848 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 849 assert_eq!(pc, 0x24); 850 } 851 }, 852 { 853 1 ; 854 wasmtime::DebugEvent::Breakpoint => { 855 let stack = store.debug_exit_frames().next().unwrap(); 856 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 857 assert_eq!(pc, 0x26); 858 } 859 }, 860 { 861 2 ; 862 wasmtime::DebugEvent::Breakpoint => { 863 let stack = store.debug_exit_frames().next().unwrap(); 864 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 865 assert_eq!(pc, 0x28); 866 } 867 }, 868 { 869 3 ; 870 wasmtime::DebugEvent::Breakpoint => { 871 let stack = store.debug_exit_frames().next().unwrap(); 872 let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 873 assert_eq!(pc, 0x29); 874 } 875 } 876 ); 877 878 let (handler, counter) = D2::new_and_counter(); 879 store.set_debug_handler(handler); 880 881 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 882 .await?; 883 assert_eq!(counter.load(Ordering::Relaxed), 4); 884 885 // Re-enable individual breakpoint. 886 store 887 .edit_breakpoints() 888 .unwrap() 889 .add_breakpoint(&module, 0x28) 890 .unwrap(); 891 892 // Now disable single-stepping. The single breakpoint set above 893 // should still remain. 894 store 895 .edit_breakpoints() 896 .unwrap() 897 .single_step(false) 898 .unwrap(); 899 900 let (handler, counter) = D::new_and_counter(); 901 store.set_debug_handler(handler); 902 903 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 904 .await?; 905 assert_eq!(counter.load(Ordering::Relaxed), 1); 906 907 Ok(()) 908 } 909 910 #[tokio::test] 911 #[cfg_attr(miri, ignore)] 912 async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> { 913 let _ = env_logger::try_init(); 914 915 let (module, mut store) = get_module_and_store( 916 |config| { 917 config.wasm_exceptions(true); 918 config.compiler_inlining(true); 919 unsafe { 920 config.cranelift_flag_set("wasmtime_inlining_intra_module", "true"); 921 } 922 }, 923 r#" 924 (module 925 (func $f (export "f") (param i32 i32) (result i32) 926 local.get 0 927 local.get 1 928 i32.add) 929 930 (func (export "main") (param i32 i32) (result i32) 931 local.get 0 932 local.get 1 933 call $f)) 934 "#, 935 )?; 936 937 debug_event_checker!( 938 D, store, 939 { 0 ; 940 wasmtime::DebugEvent::Breakpoint => {} 941 }, 942 { 1 ; 943 wasmtime::DebugEvent::Breakpoint => {} 944 } 945 ); 946 947 let (handler, counter) = D::new_and_counter(); 948 store.set_debug_handler(handler); 949 store 950 .edit_breakpoints() 951 .unwrap() 952 .add_breakpoint(&module, 0x2d)?; // `i32.add` in `$f`. 953 954 let instance = Instance::new_async(&mut store, &module, &[]).await?; 955 let func_main = instance.get_func(&mut store, "main").unwrap(); 956 let func_f = instance.get_func(&mut store, "f").unwrap(); 957 let mut results = [Val::I32(0)]; 958 // Breakpoint in `$f` should have been hit in `main` even if it 959 // was inlined. 960 func_main 961 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 962 .await?; 963 assert_eq!(counter.load(Ordering::Relaxed), 1); 964 assert_eq!(results[0].unwrap_i32(), 3); 965 966 // Breakpoint in `$f` should be hit when called directly, too. 967 func_f 968 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 969 .await?; 970 assert_eq!(counter.load(Ordering::Relaxed), 2); 971 assert_eq!(results[0].unwrap_i32(), 3); 972 973 Ok(()) 974 } 975 976 #[tokio::test] 977 #[cfg_attr(miri, ignore)] 978 async fn epoch_events() -> wasmtime::Result<()> { 979 let _ = env_logger::try_init(); 980 981 let (module, mut store) = get_module_and_store( 982 |config| { 983 config.epoch_interruption(true); 984 }, 985 r#" 986 (module 987 (func $f (export "f") (param i32 i32) (result i32) 988 local.get 0 989 local.get 1 990 i32.add)) 991 "#, 992 )?; 993 994 debug_event_checker!( 995 D, store, 996 { 0 ; 997 wasmtime::DebugEvent::EpochYield => {} 998 } 999 ); 1000 1001 let (handler, counter) = D::new_and_counter(); 1002 store.set_debug_handler(handler); 1003 1004 store.set_epoch_deadline(1); 1005 store.epoch_deadline_async_yield_and_update(1); 1006 store.engine().increment_epoch(); 1007 1008 let instance = Instance::new_async(&mut store, &module, &[]).await?; 1009 let func_f = instance.get_func(&mut store, "f").unwrap(); 1010 let mut results = [Val::I32(0)]; 1011 func_f 1012 .call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 1013 .await?; 1014 assert_eq!(counter.load(Ordering::Relaxed), 1); 1015 assert_eq!(results[0].unwrap_i32(), 3); 1016 1017 Ok(()) 1018 } 1019 1020 #[tokio::test] 1021 #[cfg_attr(miri, ignore)] 1022 async fn invalidated_frame_handles() -> wasmtime::Result<()> { 1023 let (module, mut store) = get_module_and_store( 1024 |_config| {}, 1025 r#" 1026 (module 1027 (import "" "" (func)) 1028 (func (export "main") 1029 (local i32 i32) 1030 i32.const 1 1031 local.set 0 1032 i32.const 2 1033 local.set 1 1034 call 2 1035 call 0) 1036 (func 1037 (local i32 i32) 1038 i32.const 3 1039 local.set 0 1040 i32.const 4 1041 local.set 1 1042 call 0)) 1043 "#, 1044 )?; 1045 1046 let handle: Arc<Mutex<Option<FrameHandle>>> = Arc::new(Mutex::new(None)); 1047 1048 let hostfunc = Func::wrap_async(&mut store, move |mut caller, _args: ()| { 1049 let handle = handle.clone(); 1050 Box::new(async move { 1051 let frame = handle.lock().unwrap().take(); 1052 if let Some(frame) = frame { 1053 // Ensure that the handle has been invalidated. 1054 assert!(!frame.is_valid(&mut caller)); 1055 // Ensure that attempts to fetch data from the frame 1056 // fail with a clean `Err`, not a panic or crash. 1057 let result = frame.wasm_function_index_and_pc(&mut caller); 1058 assert!(result.is_err()); 1059 // Ensure that we can get a new frame handle and use it. 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(), 1); 1063 assert_eq!(frame.local(&mut caller, 1)?.unwrap_i32(), 2); 1064 } else { 1065 let frame = caller.debug_exit_frames().next().unwrap(); 1066 assert_eq!(frame.num_locals(&mut caller)?, 2); 1067 assert_eq!(frame.local(&mut caller, 0)?.unwrap_i32(), 3); 1068 assert_eq!(frame.local(&mut caller, 1)?.unwrap_i32(), 4); 1069 *handle.lock().unwrap() = Some(frame); 1070 } 1071 Ok(()) 1072 }) 1073 }); 1074 1075 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(hostfunc)]).await?; 1076 let main = instance.get_func(&mut store, "main").unwrap(); 1077 main.call_async(&mut store, &[], &mut []).await?; 1078 1079 Ok(()) 1080 } 1081 1082 #[tokio::test] 1083 #[cfg_attr(miri, ignore)] 1084 async fn invalidated_frame_handles_in_dropped_future() -> wasmtime::Result<()> { 1085 let (module, mut store) = get_module_and_store( 1086 |_config| {}, 1087 r#" 1088 (module 1089 (import "" "" (func)) 1090 (func (export "main") 1091 call 0)) 1092 "#, 1093 )?; 1094 1095 let handle: Arc<Mutex<Option<FrameHandle>>> = Arc::new(Mutex::new(None)); 1096 let handle_clone = handle.clone(); 1097 1098 let hostfunc = Func::wrap_async(&mut store, move |mut caller, _args: ()| { 1099 let handle_clone = handle_clone.clone(); 1100 Box::new(async move { 1101 let frame = caller.debug_exit_frames().next().unwrap(); 1102 *handle_clone.lock().unwrap() = Some(frame); 1103 tokio::task::yield_now().await; 1104 }) 1105 }); 1106 1107 let instance = Instance::new_async(&mut store, &module, &[Extern::Func(hostfunc)]).await?; 1108 let main = instance.get_func(&mut store, "main").unwrap(); 1109 let future = Box::pin(main.call_async(&mut store, &[], &mut [])); 1110 1111 // Poll once, then drop. 1112 let poll_once = PollOnce::new(future); 1113 let future = poll_once.await; 1114 1115 drop(future); 1116 1117 // The frame handle should now be invalid. 1118 let mut handle = handle.lock().unwrap(); 1119 let frame = handle.take().unwrap(); 1120 assert!(!frame.is_valid(&mut store)); 1121 1122 Ok(()) 1123 } 1124 1125 #[test] 1126 #[cfg_attr(miri, ignore)] 1127 fn module_bytecode() -> wasmtime::Result<()> { 1128 let wasm = wat::parse_str( 1129 r#" 1130 (module 1131 (func (export "add") (param i32 i32) (result i32) 1132 local.get 0 1133 local.get 1 1134 i32.add 1135 ) 1136 ) 1137 "#, 1138 ) 1139 .unwrap(); 1140 1141 let mut config = Config::default(); 1142 config.guest_debug(true); 1143 let engine = Engine::new(&config)?; 1144 let module = Module::new(&engine, &wasm)?; 1145 1146 assert_eq!(module.debug_bytecode(), Some(&wasm[..])); 1147 1148 Ok(()) 1149 } 1150 1151 #[test] 1152 #[cfg_attr(miri, ignore)] 1153 fn module_bytecode_absent_without_debug() -> wasmtime::Result<()> { 1154 let wasm = wat::parse_str("(module)").unwrap(); 1155 1156 let mut config = Config::default(); 1157 config.guest_debug(false); 1158 let engine = Engine::new(&config)?; 1159 let module = Module::new(&engine, &wasm)?; 1160 1161 assert_eq!(module.debug_bytecode(), None); 1162 1163 Ok(()) 1164 } 1165 1166 #[test] 1167 #[cfg_attr(miri, ignore)] 1168 fn component_bytecode() -> wasmtime::Result<()> { 1169 use wasmtime::component::{Component, Linker}; 1170 1171 // Build the bytecode for each core module by compiling them 1172 // standalone. 1173 let m1_body = r#"(func (export "f1") (result i32) i32.const 42)"#; 1174 let m2_body = r#"(func (export "f2") (result i32) i32.const 99)"#; 1175 let m1_wasm = wat::parse_str(&format!("(module $m1 {m1_body})")).unwrap(); 1176 let m2_wasm = wat::parse_str(&format!("(module $m2 {m2_body})")).unwrap(); 1177 1178 // Build a component that embeds both core modules inline. 1179 let component_wasm = wat::parse_str(&format!( 1180 r#"(component 1181 (core module $m1 {m1_body}) 1182 (core instance $i1 (instantiate (module $m1))) 1183 (core module $m2 {m2_body}) 1184 (core instance $i2 (instantiate (module $m2)))) 1185 "#, 1186 )) 1187 .unwrap(); 1188 1189 let mut config = Config::default(); 1190 config.guest_debug(true); 1191 let engine = Engine::new(&config)?; 1192 1193 let component = Component::new(&engine, &component_wasm)?; 1194 let linker: Linker<()> = Linker::new(&engine); 1195 let mut store = Store::new(&engine, ()); 1196 linker.instantiate(&mut store, &component)?; 1197 1198 let modules = store.debug_all_modules(); 1199 assert_eq!(modules.len(), 2); 1200 1201 // Modules should be registered in offset order. The API doesn't 1202 // guarantee this, but this suffices for a test. 1203 assert_eq!(modules[0].debug_bytecode().unwrap(), &m1_wasm[..]); 1204 assert_eq!(modules[1].debug_bytecode().unwrap(), &m2_wasm[..]); 1205 1206 Ok(()) 1207 } 1208 1209 #[test] 1210 #[cfg_attr(miri, ignore)] 1211 fn debug_ids() -> wasmtime::Result<()> { 1212 let mut config = Config::default(); 1213 config.guest_debug(true); 1214 config.wasm_exceptions(true); 1215 let engine = Engine::new(&config)?; 1216 let mut store = Store::new(&engine, ()); 1217 let module1 = Module::new( 1218 &engine, 1219 r#" 1220 (module 1221 (memory 1 1) 1222 (memory 1 1) 1223 (global (mut i32) (i32.const 0)) 1224 (global (mut i32) (i32.const 1)) 1225 (table 1 1 funcref) 1226 (table 1 1 funcref) 1227 (tag (param i32)) 1228 (tag (param i64))) 1229 "#, 1230 )?; 1231 1232 let module2 = Module::new( 1233 &engine, 1234 r#" 1235 (module 1236 (memory (export "m") 1 1)) 1237 "#, 1238 )?; 1239 1240 let instance1 = Instance::new(&mut store, &module1, &[])?; 1241 let instance2 = Instance::new(&mut store, &module2, &[])?; 1242 let instance3 = Instance::new(&mut store, &module1, &[])?; 1243 1244 assert_ne!( 1245 module1.debug_index_in_engine(), 1246 module2.debug_index_in_engine() 1247 ); 1248 assert_ne!( 1249 instance1.debug_index_in_store(), 1250 instance2.debug_index_in_store() 1251 ); 1252 assert_ne!( 1253 instance1 1254 .debug_memory(&mut store, 0) 1255 .unwrap() 1256 .debug_index_in_store(), 1257 instance1 1258 .debug_memory(&mut store, 1) 1259 .unwrap() 1260 .debug_index_in_store() 1261 ); 1262 assert_ne!( 1263 instance1 1264 .debug_memory(&mut store, 0) 1265 .unwrap() 1266 .debug_index_in_store(), 1267 instance2 1268 .debug_memory(&mut store, 0) 1269 .unwrap() 1270 .debug_index_in_store() 1271 ); 1272 assert_ne!( 1273 instance1 1274 .debug_memory(&mut store, 0) 1275 .unwrap() 1276 .debug_index_in_store(), 1277 instance3 1278 .debug_memory(&mut store, 0) 1279 .unwrap() 1280 .debug_index_in_store() 1281 ); 1282 assert_ne!( 1283 instance1 1284 .debug_global(&mut store, 0) 1285 .unwrap() 1286 .debug_index_in_store(), 1287 instance3 1288 .debug_global(&mut store, 0) 1289 .unwrap() 1290 .debug_index_in_store() 1291 ); 1292 assert_ne!( 1293 instance1 1294 .debug_table(&mut store, 0) 1295 .unwrap() 1296 .debug_index_in_store(), 1297 instance3 1298 .debug_table(&mut store, 0) 1299 .unwrap() 1300 .debug_index_in_store() 1301 ); 1302 assert_ne!( 1303 instance1 1304 .debug_tag(&mut store, 0) 1305 .unwrap() 1306 .debug_index_in_store(), 1307 instance3 1308 .debug_tag(&mut store, 0) 1309 .unwrap() 1310 .debug_index_in_store() 1311 ); 1312 1313 let m_via_export = instance2 1314 .get_export(&mut store, "m") 1315 .unwrap() 1316 .into_memory() 1317 .unwrap(); 1318 let m_via_introspection = instance2.debug_memory(&mut store, 0).unwrap(); 1319 assert_eq!( 1320 m_via_export.debug_index_in_store(), 1321 m_via_introspection.debug_index_in_store() 1322 ); 1323 1324 Ok(()) 1325 } 1326 1327 #[tokio::test] 1328 #[cfg_attr(miri, ignore)] 1329 async fn single_step_before_instantiation() -> wasmtime::Result<()> { 1330 let _ = env_logger::try_init(); 1331 1332 let mut config = Config::default(); 1333 config.guest_debug(true); 1334 let engine = Engine::new(&config)?; 1335 let module = Module::new( 1336 &engine, 1337 r#" 1338 (module 1339 (func (export "main") (param i32 i32) (result i32) 1340 local.get 0 1341 local.get 1 1342 i32.add)) 1343 "#, 1344 )?; 1345 let mut store = Store::new(&engine, ()); 1346 1347 // Enable single-stepping *before* instantiation. The module has not 1348 // been registered with this store yet. 1349 store.edit_breakpoints().unwrap().single_step(true).unwrap(); 1350 assert!(store.is_single_step()); 1351 1352 #[derive(Clone)] 1353 struct CountingHandler(Arc<AtomicUsize>); 1354 impl DebugHandler for CountingHandler { 1355 type Data = (); 1356 async fn handle(&self, _store: StoreContextMut<'_, ()>, event: DebugEvent<'_>) { 1357 match event { 1358 DebugEvent::Breakpoint => { 1359 self.0.fetch_add(1, Ordering::Relaxed); 1360 } 1361 _ => {} 1362 } 1363 } 1364 } 1365 1366 let counter = Arc::new(AtomicUsize::new(0)); 1367 store.set_debug_handler(CountingHandler(counter.clone())); 1368 1369 let instance = Instance::new_async(&mut store, &module, &[]).await?; 1370 let func = instance.get_func(&mut store, "main").unwrap(); 1371 let mut results = [Val::I32(0)]; 1372 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 1373 .await?; 1374 assert_eq!(results[0].unwrap_i32(), 3); 1375 1376 assert_eq!(counter.load(Ordering::Relaxed), 4); 1377 1378 Ok(()) 1379 } 1380 1381 #[tokio::test] 1382 #[cfg_attr(miri, ignore)] 1383 async fn early_epoch_yield_still_has_vmctx() -> wasmtime::Result<()> { 1384 let _ = env_logger::try_init(); 1385 1386 let mut config = Config::default(); 1387 config.guest_debug(true); 1388 config.epoch_interruption(true); 1389 let engine = Engine::new(&config)?; 1390 let module = Module::new( 1391 &engine, 1392 r#" 1393 (module 1394 (func (export "main") (param i32 i32) (result i32) 1395 local.get 0 1396 local.get 1 1397 i32.add)) 1398 "#, 1399 )?; 1400 let mut store = Store::new(&engine, ()); 1401 store.set_epoch_deadline(1); 1402 store.epoch_deadline_async_yield_and_update(1); 1403 engine.increment_epoch(); 1404 1405 #[derive(Clone)] 1406 struct H; 1407 impl DebugHandler for H { 1408 type Data = (); 1409 async fn handle(&self, mut store: StoreContextMut<'_, ()>, _event: DebugEvent<'_>) { 1410 // Ensure we can access the instance (which accesses the 1411 // vmctx slot in the frame's debug info). 1412 let frame = store.debug_exit_frames().next().unwrap(); 1413 let _instance = frame.instance(&mut store); 1414 } 1415 } 1416 1417 store.set_debug_handler(H); 1418 1419 let instance = Instance::new_async(&mut store, &module, &[]).await?; 1420 let func = instance.get_func(&mut store, "main").unwrap(); 1421 let mut results = [Val::I32(0)]; 1422 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 1423 .await?; 1424 assert_eq!(results[0].unwrap_i32(), 3); 1425 1426 Ok(()) 1427 } 1428 1429 #[tokio::test] 1430 #[cfg_attr(miri, ignore)] 1431 async fn breakpoint_slips_to_first_opcode() -> wasmtime::Result<()> { 1432 let _ = env_logger::try_init(); 1433 1434 // Breakpoints set at the function body start (which includes the 1435 // local declarations and precedes the first opcode) should be 1436 // "slipped" forward to the first opcode. This matches how LLDB 1437 // sets breakpoints using DWARF `DW_AT_low_pc`. 1438 // 1439 // For the WAT below, `wasm-objdump -d` shows: 1440 // 1441 // ``` 1442 // 000023 func[0] <main>: 1443 // 000024: 20 00 | local.get 0 1444 // 000026: 20 01 | local.get 1 1445 // 000028: 6a | i32.add 1446 // 000029: 0b | end 1447 // ``` 1448 // 1449 // 0x23 is the function body start (locals count byte), while 0x24 1450 // is the first opcode. Setting a breakpoint at 0x23 should slip 1451 // to 0x24. 1452 let (module, mut store) = get_module_and_store( 1453 |_config| {}, 1454 r#" 1455 (module 1456 (func (export "main") (param i32 i32) (result i32) 1457 local.get 0 1458 local.get 1 1459 i32.add)) 1460 "#, 1461 )?; 1462 1463 debug_event_checker!( 1464 D, store, 1465 { 0 ; 1466 wasmtime::DebugEvent::Breakpoint => { 1467 let stack = store.debug_exit_frames().next().unwrap(); 1468 let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); 1469 assert_eq!(func.as_u32(), 0); 1470 // The breakpoint should fire at the first opcode 1471 // (0x24), not at the function body start (0x23). 1472 assert_eq!(pc, 0x24); 1473 } 1474 } 1475 ); 1476 1477 let (handler, counter) = D::new_and_counter(); 1478 store.set_debug_handler(handler); 1479 1480 store 1481 .edit_breakpoints() 1482 .unwrap() 1483 .add_breakpoint(&module, 0x23)?; 1484 1485 let instance = Instance::new_async(&mut store, &module, &[]).await?; 1486 let func = instance.get_func(&mut store, "main").unwrap(); 1487 let mut results = [Val::I32(0)]; 1488 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 1489 .await?; 1490 assert_eq!(counter.load(Ordering::Relaxed), 1); 1491 assert_eq!(results[0].unwrap_i32(), 3); 1492 1493 // The actual breakpoint stored should be at the slipped PC. 1494 let breakpoints = store.breakpoints().unwrap().collect::<Vec<_>>(); 1495 assert_eq!(breakpoints.len(), 1); 1496 assert_eq!(breakpoints[0].pc, 0x24); 1497 1498 // Removing with the originally requested PC should work. 1499 store 1500 .edit_breakpoints() 1501 .unwrap() 1502 .remove_breakpoint(&module, 0x23)?; 1503 func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) 1504 .await?; 1505 // Counter should not have incremented now that we removed the 1506 // breakpoint. 1507 assert_eq!(counter.load(Ordering::Relaxed), 1); 1508 1509 Ok(()) 1510 } 1511