@@ -80,3 +80,166 @@ fn main() -> hyperlight_host::Result<()> {
80
80
81
81
Ok ( ( ) )
82
82
}
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