@@ -80,3 +80,166 @@ fn main() -> hyperlight_host::Result<()> {
8080
8181 Ok ( ( ) )
8282}
83+
84+ #[ cfg( gdb) ]
85+ #[ cfg( test) ]
86+ mod tests {
87+ use std:: fs:: File ;
88+ use std:: io;
89+ use std:: process:: { Command , Stdio } ;
90+ use std:: time:: Duration ;
91+
92+ use hyperlight_host:: { new_error, Result } ;
93+ use io:: { BufReader , BufWriter , Read , Write } ;
94+
95+ use super :: * ;
96+
97+ fn write_cmds_file ( cmd_file_path : & str , out_file_path : & str ) -> io:: Result < ( ) > {
98+ let manifest_dir = std:: env:: var ( "CARGO_MANIFEST_DIR" ) . expect ( "Failed to get manifest dir" ) ;
99+ let file = File :: create ( cmd_file_path) ?;
100+ let mut writer = BufWriter :: new ( file) ;
101+
102+ // write from string to file
103+ writer. write_all (
104+ format ! (
105+ "file {manifest_dir}/../tests/rust_guests/bin/debug/simpleguest
106+ target remote :8080
107+
108+ set pagination off
109+ set logging file {out_file_path}
110+ set logging enabled on
111+
112+ break hyperlight_main
113+ commands
114+ echo \" Stopped at hyperlight_main breakpoint\\ n\"
115+ backtrace
116+ continue
117+ end
118+
119+ continue
120+
121+ set logging enabled off
122+ quit
123+ "
124+ )
125+ . as_bytes ( ) ,
126+ ) ?;
127+
128+ writer. flush ( )
129+ }
130+
131+ fn run_guest_and_gdb ( cmd_file_path : & str , out_file_path : & str ) -> Result < ( ) > {
132+ // write gdb commands to file
133+
134+ write_cmds_file ( & cmd_file_path, & out_file_path)
135+ . expect ( "Failed to write gdb commands to file" ) ;
136+
137+ let mut guest_child = Command :: new ( "cargo" )
138+ . arg ( "run" )
139+ . arg ( "--example" )
140+ . arg ( "guest-debugging" )
141+ . arg ( "--features" )
142+ . arg ( "gdb" )
143+ . stdin ( Stdio :: piped ( ) )
144+ . stdout ( Stdio :: piped ( ) )
145+ . spawn ( )
146+ . map_err ( |e| new_error ! ( "Failed to start guest process: {}" , e) ) ?;
147+
148+ // wait 3 seconds for the gdb to connect
149+ thread:: sleep ( Duration :: from_secs ( 3 ) ) ;
150+
151+ let mut gdb = Command :: new ( "rust-gdb" )
152+ . arg ( "--nw" )
153+ . arg ( "-x" )
154+ . arg ( cmd_file_path)
155+ . spawn ( )
156+ . map_err ( |e| new_error ! ( "Failed to start gdb process: {}" , e) ) ?;
157+
158+ // wait 3 seconds for the gdb to connect
159+ thread:: sleep ( Duration :: from_secs ( 10 ) ) ;
160+
161+ // check if the guest process has finished
162+ match guest_child. try_wait ( ) {
163+ Ok ( Some ( status) ) => {
164+ if !status. success ( ) {
165+ Err ( new_error ! (
166+ "Guest process exited with non-zero status: {}" ,
167+ status
168+ ) ) ?;
169+ }
170+ }
171+ Ok ( None ) => {
172+ guest_child
173+ . kill ( )
174+ . map_err ( |e| new_error ! ( "Failed to kill child process: {}" , e) ) ?;
175+ }
176+ Err ( e) => {
177+ Err ( new_error ! ( "error attempting to wait guest: {}" , e) ) ?;
178+ }
179+ }
180+
181+ // check if the gdb process has finished
182+ match gdb. try_wait ( ) {
183+ Ok ( Some ( status) ) => {
184+ if !status. success ( ) {
185+ Err ( new_error ! (
186+ "Gdb process exited with non-zero status: {}" ,
187+ status
188+ ) ) ?;
189+ }
190+ }
191+ Ok ( None ) => {
192+ gdb. kill ( )
193+ . map_err ( |e| new_error ! ( "Failed to kill guest process: {}" , e) ) ?;
194+ }
195+ Err ( e) => {
196+ Err ( new_error ! ( "error attempting to wait gdb: {}" , e) ) ?;
197+ }
198+ }
199+
200+ check_output ( & out_file_path)
201+ }
202+
203+ fn check_output ( out_file_path : & str ) -> Result < ( ) > {
204+ let results = File :: open ( out_file_path)
205+ . map_err ( |e| new_error ! ( "Failed to open gdb.output file: {}" , e) ) ?;
206+ let mut reader = BufReader :: new ( results) ;
207+ let mut contents = String :: new ( ) ;
208+ reader. read_to_string ( & mut contents) . unwrap ( ) ;
209+
210+ if contents. contains ( "Stopped at hyperlight_main breakpoint" ) {
211+ Ok ( ( ) )
212+ } else {
213+ Err ( new_error ! (
214+ "Failed to find expected output in gdb.output file"
215+ ) )
216+ }
217+ }
218+
219+ fn cleanup ( out_file_path : & str , cmd_file_path : & str ) -> Result < ( ) > {
220+ let res1 = std:: fs:: remove_file ( out_file_path)
221+ . map_err ( |e| new_error ! ( "Failed to remove gdb.output file: {}" , e) ) ;
222+ let res2 = std:: fs:: remove_file ( cmd_file_path)
223+ . map_err ( |e| new_error ! ( "Failed to remove gdb-commands.txt file: {}" , e) ) ;
224+
225+ res1?;
226+ res2?;
227+
228+ Ok ( ( ) )
229+ }
230+
231+ #[ test]
232+ fn test_gdb_end_to_end ( ) {
233+ let out_dir = std:: env:: var ( "OUT_DIR" ) . expect ( "Failed to get out dir" ) ;
234+ let out_file_path = format ! ( "{out_dir}/gdb.output" ) ;
235+ let cmd_file_path = format ! ( "{out_dir}/gdb-commands.txt" ) ;
236+
237+ let result = run_guest_and_gdb ( & cmd_file_path, & out_file_path) ;
238+
239+ // cleanup
240+ let cleanup_result = cleanup ( & out_file_path, & cmd_file_path) ;
241+ assert ! ( cleanup_result. is_ok( ) , "{}" , cleanup_result. unwrap_err( ) ) ;
242+ // check if the test passed - done at the end to ensure cleanup is done
243+ assert ! ( result. is_ok( ) , "{}" , result. unwrap_err( ) ) ;
244+ }
245+ }
0 commit comments