@@ -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,44 @@ 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+ // calculate time relative to the host epoch
229+
230+ let cycles_spent = cycles
231+ - trace_info
232+ . guest_start_tsc
233+ . as_ref ( )
234+ . map_or_else ( || 0 , |c| * c) ;
235+ let micros = cycles_spent as f64 / trace_info. tsc_freq as f64 * 1_000_000f64 ;
236+ let guest_duration = std:: time:: Duration :: from_micros ( micros as u64 ) ;
237+
238+ let guest_start_time = trace_info
239+ . guest_start_epoch
240+ . as_ref ( )
241+ . unwrap_or ( & trace_info. epoch )
242+ . saturating_duration_since ( trace_info. epoch )
243+ . checked_add ( guest_duration)
244+ . unwrap_or ( guest_duration) ;
245+
246+ let _ = out. write_all ( & guest_start_time. as_micros ( ) . to_ne_bytes ( ) ) ;
247+ // 8 bytes frame type id
248+ let _ = out. write_all ( & frame_id. to_ne_bytes ( ) ) ;
249+ // frame data
250+ write_frame ( & mut out) ;
251+ Ok ( ( ) )
252+ }
253+
214254/// Handles OutB operations from the guest.
215255#[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level= "Trace" ) ]
216256fn handle_outb_impl (
@@ -287,6 +327,53 @@ fn handle_outb_impl(
287327 write_stack ( f, & stack) ;
288328 } )
289329 }
330+ #[ cfg( feature = "trace_guest" ) ]
331+ OutBAction :: TraceRecord => {
332+ let Ok ( len) = _hv. read_trace_reg ( crate :: hypervisor:: TraceRegister :: RAX ) else {
333+ return Ok ( ( ) ) ;
334+ } ;
335+ let Ok ( ptr) = _hv. read_trace_reg ( crate :: hypervisor:: TraceRegister :: RCX ) else {
336+ return Ok ( ( ) ) ;
337+ } ;
338+ let mut buffer = vec ! [ 0u8 ; len as usize * std:: mem:: size_of:: <TraceRecord >( ) ] ;
339+ let buffer = & mut buffer[ ..] ;
340+
341+ // Read the trace records from the guest memory
342+ // TODO: maybe this can be done without copying?
343+ mem_mgr
344+ . as_ref ( )
345+ . shared_mem
346+ . copy_to_slice ( buffer, ptr as usize - SandboxMemoryLayout :: BASE_ADDRESS )
347+ // .read::<u64>((addr - SandboxMemoryLayout::BASE_ADDRESS as u64) as usize)
348+ . map_err ( |e| {
349+ new_error ! (
350+ "Failed to copy trace records from guest memory to host: {:?}" ,
351+ e
352+ )
353+ } ) ?;
354+
355+ let traces = unsafe {
356+ std:: slice:: from_raw_parts ( buffer. as_ptr ( ) as * const TraceRecord , len as usize )
357+ } ;
358+
359+ {
360+ let trace_info = _hv. trace_info_as_mut ( ) ;
361+ // Store the start guest cycles if not already set
362+ // This is the `entrypoint` of the guest execution
363+ if trace_info. guest_start_tsc . is_none ( ) && !traces. is_empty ( ) {
364+ trace_info. guest_start_tsc = Some ( traces[ 0 ] . cycles ) ;
365+ }
366+ }
367+
368+ for record in traces {
369+ record_guest_trace_frame ( _hv. trace_info_as_ref ( ) , 4u64 , record. cycles , |f| {
370+ let _ = f. write_all ( & record. msg_len . to_ne_bytes ( ) ) ;
371+ let _ = f. write_all ( & record. msg [ ..record. msg_len ] ) ;
372+ } ) ?
373+ }
374+
375+ Ok ( ( ) )
376+ }
290377 }
291378}
292379
0 commit comments