1 //! Debug-main guest to run debugger tests. 2 //! 3 //! Invoked by tests/all/debug_component.rs with particular debuggees 4 //! loaded for each test (selected by argv) below. We print "OK" to 5 //! stderr to communicate success. 6 7 use std::time::Duration; 8 9 mod api { 10 wit_bindgen::generate!({ 11 world: "bytecodealliance:wasmtime/debug-main", 12 path: "../../crates/debugger/wit", 13 with: { 14 "wasi:io/poll@0.2.6": wasip2::io::poll, 15 } 16 }); 17 } 18 use api::bytecodealliance::wasmtime::debuggee::*; 19 20 struct Component; 21 api::export!(Component with_types_in api); 22 23 impl api::exports::bytecodealliance::wasmtime::debugger::Guest for Component { 24 fn debug(d: &Debuggee, args: Vec<String>) { 25 match args.get(1).map(|s| s.as_str()) { 26 Some("simple") => { 27 test_simple(d); 28 } 29 Some("loop") => { 30 test_loop(d); 31 } 32 other => panic!("unknown test mode: {other:?}"), 33 } 34 } 35 } 36 37 struct Resumption { 38 future: EventFuture, 39 } 40 41 impl Resumption { 42 fn single_step(d: &Debuggee) -> Self { 43 let future = d.single_step(ResumptionValue::Normal); 44 Self { future } 45 } 46 47 fn continue_(d: &Debuggee) -> Self { 48 let future = d.continue_(ResumptionValue::Normal); 49 Self { future } 50 } 51 52 fn result(self, d: &Debuggee) -> Result<Event, Error> { 53 EventFuture::finish(self.future, d) 54 } 55 } 56 57 /// Tests single-stepping. 58 /// 59 /// Tests against `debugger_debuggee_simple.wat`. 60 fn test_simple(d: &Debuggee) { 61 // Step once to reach the first instruction. 62 let r = Resumption::single_step(d); 63 let _event = r.result(d).unwrap(); 64 65 let mut pcs = vec![]; 66 67 for _ in 0..5 { 68 let frames = d.exit_frames(); 69 let pc = frames[0].get_pc(d).unwrap(); 70 pcs.push(pc); 71 72 let r = Resumption::single_step(d); 73 match r.result(d).unwrap() { 74 Event::Breakpoint => {} 75 other => panic!("unexpected event: {other:?}"), 76 } 77 } 78 79 // There should be five PCs and they should each be distinct from the previous. 80 assert_eq!(pcs.len(), 5); 81 assert!(pcs.windows(2).all(|p| p[0] != p[1])); 82 83 eprintln!("OK"); 84 } 85 86 /// Interrupt test: continue an infinite-loop debuggee, interrupt it, 87 /// verify the interrupt, then set the exit flag in memory and continue 88 /// to completion. 89 /// 90 /// Tests against `debugger_debuggee_loop.wat`. 91 fn test_loop(d: &Debuggee) { 92 // Continue execution (the debuggee should loop). 93 let r = Resumption::continue_(d); 94 95 // Yield to the event loop and let it run for a bit. 96 std::thread::sleep(Duration::from_millis(100)); 97 98 // Request interrupt. 99 d.interrupt(); 100 101 // Wait for the interrupt event. 102 let event = r.result(d).unwrap(); 103 assert!( 104 matches!(event, Event::Interrupted), 105 "expected Interrupted, got {event:?}" 106 ); 107 108 // Set the exit-flag to kill the infinite loop in the guest (the 109 // debugger environment will not otherwise end until the guest 110 // ends; we have no way of forcing an early exit yet). 111 for inst in &d.all_instances() { 112 if let Ok(mem) = inst.get_memory(d, 0) { 113 mem.set_bytes(d, 0, &[1]).unwrap(); 114 } 115 } 116 117 // Continue; the debuggee should exit normally now. 118 let r = Resumption::continue_(d); 119 let event = r.result(d).unwrap(); 120 assert!( 121 matches!(event, Event::Complete), 122 "expected Complete, got {event:?}" 123 ); 124 125 eprintln!("OK"); 126 } 127 128 fn main() {} 129