1 //! Tests for instrumentation-based debugging. 2 3 use wasmtime::{Caller, Config, Engine, Extern, Func, Instance, Module, Store}; 4 5 fn test_stack_values<C: Fn(&mut Config), F: Fn(Caller<'_, ()>) + Send + Sync + 'static>( 6 wat: &str, 7 c: C, 8 f: F, 9 ) -> anyhow::Result<()> { 10 let mut config = Config::default(); 11 config.guest_debug(true); 12 config.wasm_exceptions(true); 13 c(&mut config); 14 let engine = Engine::new(&config)?; 15 let module = Module::new(&engine, wat)?; 16 17 let mut store = Store::new(&engine, ()); 18 let func = Func::wrap(&mut store, move |caller: Caller<'_, ()>| { 19 f(caller); 20 }); 21 let instance = Instance::new(&mut store, &module, &[Extern::Func(func)])?; 22 let mut results = []; 23 instance 24 .get_func(&mut store, "main") 25 .unwrap() 26 .call(&mut store, &[], &mut results)?; 27 28 Ok(()) 29 } 30 31 #[test] 32 #[cfg_attr(miri, ignore)] 33 fn stack_values_two_frames() -> anyhow::Result<()> { 34 let _ = env_logger::try_init(); 35 36 for inlining in [false, true] { 37 test_stack_values( 38 r#" 39 (module 40 (import "" "host" (func)) 41 (func (export "main") 42 i32.const 1 43 i32.const 2 44 call 2 45 drop) 46 (func (param i32 i32) (result i32) 47 local.get 0 48 local.get 1 49 call 0 50 i32.add)) 51 "#, 52 |config| { 53 config.compiler_inlining(inlining); 54 if inlining { 55 unsafe { 56 config.cranelift_flag_set("wasmtime_inlining_intra_module", "true"); 57 } 58 } 59 }, 60 |mut caller: Caller<'_, ()>| { 61 let mut stack = caller.debug_frames().unwrap(); 62 assert!(!stack.done()); 63 assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 1); 64 assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 65); 65 66 assert_eq!(stack.num_locals(), 2); 67 assert_eq!(stack.num_stacks(), 2); 68 assert_eq!(stack.local(0).unwrap_i32(), 1); 69 assert_eq!(stack.local(1).unwrap_i32(), 2); 70 assert_eq!(stack.stack(0).unwrap_i32(), 1); 71 assert_eq!(stack.stack(1).unwrap_i32(), 2); 72 73 stack.move_to_parent(); 74 assert!(!stack.done()); 75 assert_eq!(stack.wasm_function_index_and_pc().unwrap().0.as_u32(), 0); 76 assert_eq!(stack.wasm_function_index_and_pc().unwrap().1, 55); 77 78 stack.move_to_parent(); 79 assert!(stack.done()); 80 }, 81 )?; 82 } 83 Ok(()) 84 } 85 86 #[test] 87 #[cfg_attr(miri, ignore)] 88 fn stack_values_exceptions() -> anyhow::Result<()> { 89 test_stack_values( 90 r#" 91 (module 92 (tag $t (param i32)) 93 (import "" "host" (func)) 94 (func (export "main") 95 (block $b (result i32) 96 (try_table (catch $t $b) 97 (throw $t (i32.const 42))) 98 i32.const 0) 99 (call 0) 100 (drop))) 101 "#, 102 |_config| {}, 103 |mut caller: Caller<'_, ()>| { 104 let mut stack = caller.debug_frames().unwrap(); 105 assert!(!stack.done()); 106 assert_eq!(stack.num_stacks(), 1); 107 assert_eq!(stack.stack(0).unwrap_i32(), 42); 108 stack.move_to_parent(); 109 assert!(stack.done()); 110 }, 111 ) 112 } 113 114 #[test] 115 #[cfg_attr(miri, ignore)] 116 fn stack_values_dead_gc_ref() -> anyhow::Result<()> { 117 test_stack_values( 118 r#" 119 (module 120 (type $s (struct)) 121 (import "" "host" (func)) 122 (func (export "main") 123 (struct.new $s) 124 (call 0) 125 (drop))) 126 "#, 127 |config| { 128 config.wasm_gc(true); 129 }, 130 |mut caller: Caller<'_, ()>| { 131 let mut stack = caller.debug_frames().unwrap(); 132 assert!(!stack.done()); 133 assert_eq!(stack.num_stacks(), 1); 134 assert!(stack.stack(0).unwrap_anyref().is_some()); 135 stack.move_to_parent(); 136 assert!(stack.done()); 137 }, 138 ) 139 } 140