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 {
debug(d: &Debuggee, args: Vec<String>)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 {
single_step(d: &Debuggee) -> Self42 fn single_step(d: &Debuggee) -> Self {
43 let future = d.single_step(ResumptionValue::Normal);
44 Self { future }
45 }
46
continue_(d: &Debuggee) -> Self47 fn continue_(d: &Debuggee) -> Self {
48 let future = d.continue_(ResumptionValue::Normal);
49 Self { future }
50 }
51
result(self, d: &Debuggee) -> Result<Event, Error>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`.
test_simple(d: &Debuggee)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`.
test_loop(d: &Debuggee)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
main()128 fn main() {}
129