@@ -115,6 +115,67 @@ mod tests {
115115 #[ cfg( windows) ]
116116 const GDB_COMMAND : & str = "gdb" ;
117117
118+ /// Construct the (out_file_path, cmd_file_path, manifest_dir)
119+ /// triple every gdb test needs.
120+ fn gdb_test_paths ( name : & str ) -> ( String , String , String ) {
121+ let out_dir = std:: env:: var ( "OUT_DIR" ) . expect ( "Failed to get out dir" ) ;
122+ let manifest_dir = std:: env:: var ( "CARGO_MANIFEST_DIR" )
123+ . expect ( "Failed to get manifest dir" )
124+ . replace ( '\\' , "/" ) ;
125+ let out_file_path = format ! ( "{out_dir}/{name}.output" ) ;
126+ let cmd_file_path = format ! ( "{out_dir}/{name}-commands.txt" ) ;
127+ ( out_file_path, cmd_file_path, manifest_dir)
128+ }
129+
130+ /// Build a gdb script that connects to `port`, sets a single
131+ /// breakpoint at `breakpoint`, prints `echo_msg` when hit, and
132+ /// continues to completion.
133+ fn single_breakpoint_script (
134+ manifest_dir : & str ,
135+ port : u16 ,
136+ out_file_path : & str ,
137+ breakpoint : & str ,
138+ echo_msg : & str ,
139+ ) -> String {
140+ let cmd = format ! (
141+ "file {manifest_dir}/../tests/rust_guests/bin/debug/simpleguest
142+ target remote :{port}
143+
144+ set pagination off
145+ set logging file {out_file_path}
146+ set logging enabled on
147+
148+ break {breakpoint}
149+ commands
150+ echo \" {echo_msg}\\ n\"
151+ backtrace
152+
153+ continue
154+ end
155+
156+ continue
157+
158+ set logging enabled off
159+ quit
160+ "
161+ ) ;
162+ #[ cfg( windows) ]
163+ let cmd = format ! ( "set osabi none\n {cmd}" ) ;
164+ cmd
165+ }
166+
167+ /// Spawn the gdb client to execute the script in `cmd_file_path`.
168+ fn spawn_gdb_client ( cmd_file_path : & str ) -> std:: process:: Child {
169+ Command :: new ( GDB_COMMAND )
170+ . arg ( "-nx" )
171+ . arg ( "--nw" )
172+ . arg ( "--batch" )
173+ . arg ( "-x" )
174+ . arg ( cmd_file_path)
175+ . spawn ( )
176+ . expect ( "Failed to start gdb" )
177+ }
178+
118179 fn write_cmds_file ( cmd_file_path : & str , cmd : & str ) -> io:: Result < ( ) > {
119180 let file = File :: create ( cmd_file_path) ?;
120181 let mut writer = BufWriter :: new ( file) ;
@@ -163,14 +224,7 @@ mod tests {
163224 // wait 3 seconds for the gdb to connect
164225 thread:: sleep ( Duration :: from_secs ( 3 ) ) ;
165226
166- let mut gdb = Command :: new ( GDB_COMMAND )
167- . arg ( "-nx" ) // Don't load any .gdbinit files
168- . arg ( "--nw" )
169- . arg ( "--batch" )
170- . arg ( "-x" )
171- . arg ( cmd_file_path)
172- . spawn ( )
173- . map_err ( |e| new_error ! ( "Failed to start gdb process: {}" , e) ) ?;
227+ let mut gdb = spawn_gdb_client ( cmd_file_path) ;
174228
175229 // wait 3 seconds for the gdb to connect
176230 thread:: sleep ( Duration :: from_secs ( 10 ) ) ;
@@ -245,39 +299,16 @@ mod tests {
245299 #[ test]
246300 #[ serial]
247301 fn test_gdb_end_to_end ( ) {
248- let out_dir = std:: env:: var ( "OUT_DIR" ) . expect ( "Failed to get out dir" ) ;
249- let manifest_dir = std:: env:: var ( "CARGO_MANIFEST_DIR" )
250- . expect ( "Failed to get manifest dir" )
251- . replace ( '\\' , "/" ) ;
252- let out_file_path = format ! ( "{out_dir}/gdb.output" ) ;
253- let cmd_file_path = format ! ( "{out_dir}/gdb-commands.txt" ) ;
254-
255- let cmd = format ! (
256- "file {manifest_dir}/../tests/rust_guests/bin/debug/simpleguest
257- target remote :8080
258-
259- set pagination off
260- set logging file {out_file_path}
261- set logging enabled on
262-
263- break hyperlight_main
264- commands
265- echo \" Stopped at hyperlight_main breakpoint\\ n\"
266- backtrace
267-
268- continue
269- end
270-
271- continue
272-
273- set logging enabled off
274- quit
275- "
302+ let ( out_file_path, cmd_file_path, manifest_dir) = gdb_test_paths ( "gdb" ) ;
303+
304+ let cmd = single_breakpoint_script (
305+ & manifest_dir,
306+ 8080 ,
307+ & out_file_path,
308+ "hyperlight_main" ,
309+ "Stopped at hyperlight_main breakpoint" ,
276310 ) ;
277311
278- #[ cfg( windows) ]
279- let cmd = format ! ( "set osabi none\n {}" , cmd) ;
280-
281312 let checker = |contents : String | contents. contains ( "Stopped at hyperlight_main breakpoint" ) ;
282313
283314 let result = run_guest_and_gdb ( & cmd_file_path, & out_file_path, & cmd, checker) ;
@@ -289,13 +320,8 @@ mod tests {
289320 #[ test]
290321 #[ serial]
291322 fn test_gdb_sse_check ( ) {
292- let out_dir = std:: env:: var ( "OUT_DIR" ) . expect ( "Failed to get out dir" ) ;
293- let manifest_dir = std:: env:: var ( "CARGO_MANIFEST_DIR" )
294- . expect ( "Failed to get manifest dir" )
295- . replace ( '\\' , "/" ) ;
323+ let ( out_file_path, cmd_file_path, manifest_dir) = gdb_test_paths ( "gdb-sse" ) ;
296324 println ! ( "manifest dir {manifest_dir}" ) ;
297- let out_file_path = format ! ( "{out_dir}/gdb-sse.output" ) ;
298- let cmd_file_path = format ! ( "{out_dir}/gdb-sse--commands.txt" ) ;
299325
300326 let cmd = format ! (
301327 "file {manifest_dir}/../tests/rust_guests/bin/debug/simpleguest
@@ -333,4 +359,81 @@ mod tests {
333359 cleanup ( & out_file_path, & cmd_file_path) ;
334360 assert ! ( result. is_ok( ) , "{}" , result. unwrap_err( ) ) ;
335361 }
362+
363+ #[ test]
364+ #[ serial]
365+ fn test_gdb_from_snapshot ( ) {
366+ use std:: sync:: Arc ;
367+
368+ use hyperlight_host:: HostFunctions ;
369+ use hyperlight_host:: sandbox:: snapshot:: Snapshot ;
370+
371+ const PORT : u16 = 8081 ;
372+
373+ let ( out_file_path, cmd_file_path, manifest_dir) = gdb_test_paths ( "gdb-from-snapshot" ) ;
374+ let out_dir = std:: env:: var ( "OUT_DIR" ) . unwrap ( ) ;
375+ let snap_path = format ! ( "{out_dir}/from-snapshot-debug.hls" ) ;
376+
377+ // Build a sandbox the normal way and persist its snapshot.
378+ let mut producer: MultiUseSandbox = UninitializedSandbox :: new (
379+ hyperlight_host:: GuestBinary :: FilePath (
380+ hyperlight_testing:: simple_guest_as_string ( ) . unwrap ( ) ,
381+ ) ,
382+ None ,
383+ )
384+ . unwrap ( )
385+ . evolve ( )
386+ . unwrap ( ) ;
387+ producer. snapshot ( ) . unwrap ( ) . to_file ( & snap_path) . unwrap ( ) ;
388+
389+ // Order matters. The gdb stub event loop must enter (i.e.
390+ // `VcpuStopped` must be sent on the channel) before the gdb
391+ // client connects, otherwise the wire protocol desyncs. The
392+ // evolve case gets this for free because `evolve()` runs
393+ // `vm.initialise()` which trips the entry breakpoint
394+ // immediately. For a `Call` snapshot `vm.initialise` is a
395+ // no-op, so we trigger the breakpoint by running `sbox.call`
396+ // here before the client is launched below.
397+ let snap_path_thread = snap_path. clone ( ) ;
398+ let sandbox_thread = thread:: spawn ( move || -> Result < ( ) > {
399+ let mut cfg = SandboxConfiguration :: default ( ) ;
400+ cfg. set_guest_debug_info ( DebugInfo { port : PORT } ) ;
401+
402+ let loaded = Arc :: new ( Snapshot :: from_file ( & snap_path_thread) ?) ;
403+ let mut sbox =
404+ MultiUseSandbox :: from_snapshot ( loaded, HostFunctions :: default ( ) , Some ( cfg) ) ?;
405+ sbox. call :: < i32 > (
406+ "PrintOutput" ,
407+ "Hello from a from_snapshot sandbox\n " . to_string ( ) ,
408+ ) ?;
409+ Ok ( ( ) )
410+ } ) ;
411+
412+ // Wait for the sandbox thread to bind the listener, install
413+ // the one-shot breakpoint, and trip it.
414+ thread:: sleep ( Duration :: from_secs ( 3 ) ) ;
415+
416+ let cmd = single_breakpoint_script (
417+ & manifest_dir,
418+ PORT ,
419+ & out_file_path,
420+ "print_output" ,
421+ "Stopped at print_output breakpoint" ,
422+ ) ;
423+ write_cmds_file ( & cmd_file_path, & cmd) . expect ( "Failed to write gdb commands" ) ;
424+
425+ let mut gdb = spawn_gdb_client ( & cmd_file_path) ;
426+ let _ = gdb. wait ( ) ;
427+ let sandbox_result = sandbox_thread
428+ . join ( )
429+ . expect ( "from_snapshot sandbox thread panicked" ) ;
430+ let _ = std:: fs:: remove_file ( & snap_path) ;
431+
432+ let checker = |contents : String | contents. contains ( "Stopped at print_output breakpoint" ) ;
433+ let result = check_output ( & out_file_path, checker) ;
434+
435+ cleanup ( & out_file_path, & cmd_file_path) ;
436+ sandbox_result. expect ( "from_snapshot sandbox returned error" ) ;
437+ result. expect ( "gdb output missing expected breakpoint hit" ) ;
438+ }
336439}
0 commit comments