@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
- #[ cfg( feature = "unwind_guest " ) ]
17
+ #[ cfg( feature = "trace_guest " ) ]
18
18
use std:: io:: Write ;
19
19
use std:: sync:: { Arc , Mutex } ;
20
20
@@ -26,14 +26,16 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ParameterValue;
26
26
use hyperlight_common:: flatbuffer_wrappers:: guest_error:: ErrorCode ;
27
27
use hyperlight_common:: flatbuffer_wrappers:: guest_log_data:: GuestLogData ;
28
28
use hyperlight_common:: outb:: { Exception , OutBAction } ;
29
+ #[ cfg( feature = "trace_guest" ) ]
30
+ use hyperlight_guest_tracing:: TraceRecord ;
29
31
use log:: { Level , Record } ;
30
32
use tracing:: { Span , instrument} ;
31
33
use tracing_log:: format_trace;
32
34
33
35
use super :: host_funcs:: FunctionRegistry ;
34
36
use super :: mem_mgr:: MemMgrWrapper ;
35
37
use crate :: hypervisor:: handlers:: { OutBHandler , OutBHandlerFunction , OutBHandlerWrapper } ;
36
- #[ cfg( feature = "unwind_guest " ) ]
38
+ #[ cfg( feature = "trace_guest " ) ]
37
39
use crate :: mem:: layout:: SandboxMemoryLayout ;
38
40
use crate :: mem:: mgr:: SandboxMemoryManager ;
39
41
use crate :: mem:: shared_mem:: HostSharedMemory ;
@@ -211,6 +213,53 @@ pub(super) fn record_trace_frame<F: FnOnce(&mut std::fs::File)>(
211
213
Ok ( ( ) )
212
214
}
213
215
216
+ #[ cfg( feature = "trace_guest" ) ]
217
+ pub ( super ) fn record_guest_trace_frame < F : FnOnce ( & mut std:: fs:: File ) > (
218
+ trace_info : & TraceInfo ,
219
+ frame_id : u64 ,
220
+ cycles : u64 ,
221
+ write_frame : F ,
222
+ ) -> Result < ( ) > {
223
+ let Ok ( mut out) = trace_info. file . lock ( ) else {
224
+ return Ok ( ( ) ) ;
225
+ } ;
226
+ // frame structure:
227
+ // 16 bytes timestamp
228
+
229
+ // The number of cycles spent in the guest relative to the first received trace record
230
+ let cycles_spent = cycles
231
+ - trace_info
232
+ . guest_start_tsc
233
+ . as_ref ( )
234
+ . map_or_else ( || 0 , |c| * c) ;
235
+ // Convert cycles to microseconds based on the TSC frequency
236
+ let micros = cycles_spent as f64 / trace_info. tsc_freq as f64 * 1_000_000f64 ;
237
+
238
+ // Convert to a Duration
239
+ let guest_duration = std:: time:: Duration :: from_micros ( micros as u64 ) ;
240
+
241
+ // Calculate the time when the guest started execution relative to the host epoch
242
+ // Note: This is relative to the time saved when the `TraceInfo` was created (before the
243
+ // Hypervisor is created).
244
+ let guest_start_time = trace_info
245
+ . guest_start_epoch
246
+ . as_ref ( )
247
+ . unwrap_or ( & trace_info. epoch )
248
+ . saturating_duration_since ( trace_info. epoch ) ;
249
+
250
+ // Calculate the timestamp when the actual frame was recorded relative to the host epoch
251
+ let timestamp = guest_start_time
252
+ . checked_add ( guest_duration)
253
+ . unwrap_or ( guest_duration) ;
254
+
255
+ let _ = out. write_all ( & timestamp. as_micros ( ) . to_ne_bytes ( ) ) ;
256
+ // 8 bytes frame type id
257
+ let _ = out. write_all ( & frame_id. to_ne_bytes ( ) ) ;
258
+ // frame data
259
+ write_frame ( & mut out) ;
260
+ Ok ( ( ) )
261
+ }
262
+
214
263
/// Handles OutB operations from the guest.
215
264
#[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level= "Trace" ) ]
216
265
fn handle_outb_impl (
@@ -287,6 +336,54 @@ fn handle_outb_impl(
287
336
write_stack ( f, & stack) ;
288
337
} )
289
338
}
339
+ #[ cfg( feature = "trace_guest" ) ]
340
+ OutBAction :: TraceRecord => {
341
+ let Ok ( len) = _hv. read_trace_reg ( crate :: hypervisor:: TraceRegister :: RAX ) else {
342
+ return Ok ( ( ) ) ;
343
+ } ;
344
+ let Ok ( ptr) = _hv. read_trace_reg ( crate :: hypervisor:: TraceRegister :: RCX ) else {
345
+ return Ok ( ( ) ) ;
346
+ } ;
347
+ let mut buffer = vec ! [ 0u8 ; len as usize * std:: mem:: size_of:: <TraceRecord >( ) ] ;
348
+ let buffer = & mut buffer[ ..] ;
349
+
350
+ // Read the trace records from the guest memory
351
+ // TODO: maybe this can be done without copying?
352
+ mem_mgr
353
+ . as_ref ( )
354
+ . shared_mem
355
+ . copy_to_slice ( buffer, ptr as usize - SandboxMemoryLayout :: BASE_ADDRESS )
356
+ // .read::<u64>((addr - SandboxMemoryLayout::BASE_ADDRESS as u64) as usize)
357
+ . map_err ( |e| {
358
+ new_error ! (
359
+ "Failed to copy trace records from guest memory to host: {:?}" ,
360
+ e
361
+ )
362
+ } ) ?;
363
+
364
+ let traces = unsafe {
365
+ std:: slice:: from_raw_parts ( buffer. as_ptr ( ) as * const TraceRecord , len as usize )
366
+ } ;
367
+
368
+ {
369
+ let trace_info = _hv. trace_info_as_mut ( ) ;
370
+ // Store the start guest cycles if not already set
371
+ // This is the `entrypoint` of the guest execution
372
+ // This should be set only once, at the start of the guest execution
373
+ if trace_info. guest_start_tsc . is_none ( ) && !traces. is_empty ( ) {
374
+ trace_info. guest_start_tsc = Some ( traces[ 0 ] . cycles ) ;
375
+ }
376
+ }
377
+
378
+ for record in traces {
379
+ record_guest_trace_frame ( _hv. trace_info_as_ref ( ) , 4u64 , record. cycles , |f| {
380
+ let _ = f. write_all ( & record. msg_len . to_ne_bytes ( ) ) ;
381
+ let _ = f. write_all ( & record. msg [ ..record. msg_len ] ) ;
382
+ } ) ?
383
+ }
384
+
385
+ Ok ( ( ) )
386
+ }
290
387
}
291
388
}
292
389
0 commit comments