@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- #[ cfg( feature = "unwind_guest " ) ]
17+ #[ cfg( feature = "trace_guest " ) ]
1818use std:: io:: Write ;
1919use std:: sync:: { Arc , Mutex } ;
2020
@@ -26,14 +26,16 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ParameterValue;
2626use hyperlight_common:: flatbuffer_wrappers:: guest_error:: ErrorCode ;
2727use hyperlight_common:: flatbuffer_wrappers:: guest_log_data:: GuestLogData ;
2828use hyperlight_common:: outb:: { Exception , OutBAction } ;
29+ #[ cfg( feature = "trace_guest" ) ]
30+ use hyperlight_guest_tracing:: TraceRecord ;
2931use log:: { Level , Record } ;
3032use tracing:: { Span , instrument} ;
3133use tracing_log:: format_trace;
3234
3335use super :: host_funcs:: FunctionRegistry ;
3436use super :: mem_mgr:: MemMgrWrapper ;
3537use crate :: hypervisor:: handlers:: { OutBHandler , OutBHandlerFunction , OutBHandlerWrapper } ;
36- #[ cfg( feature = "unwind_guest " ) ]
38+ #[ cfg( feature = "trace_guest " ) ]
3739use crate :: mem:: layout:: SandboxMemoryLayout ;
3840use crate :: mem:: mgr:: SandboxMemoryManager ;
3941use crate :: mem:: shared_mem:: HostSharedMemory ;
@@ -211,6 +213,53 @@ pub(super) fn record_trace_frame<F: FnOnce(&mut std::fs::File)>(
211213 Ok ( ( ) )
212214}
213215
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+
214263/// Handles OutB operations from the guest.
215264#[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level= "Trace" ) ]
216265fn handle_outb_impl (
@@ -287,6 +336,54 @@ fn handle_outb_impl(
287336 write_stack ( f, & stack) ;
288337 } )
289338 }
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+ }
290387 }
291388}
292389
0 commit comments