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