@@ -90,7 +90,7 @@ pub type ExtraAllowedSyscall = i64;
9090/// Determine whether a suitable hypervisor is available to run
9191/// this sandbox.
9292///
93- // Returns a boolean indicating whether a suitable hypervisor is present.
93+ /// Returns a boolean indicating whether a suitable hypervisor is present.
9494#[ instrument( skip_all, parent = Span :: current( ) ) ]
9595pub fn is_hypervisor_present ( ) -> bool {
9696 hypervisor:: get_available_hypervisor ( ) . is_some ( )
@@ -103,18 +103,23 @@ pub fn is_hypervisor_present() -> bool {
103103pub ( crate ) struct TraceInfo {
104104 /// The epoch against which trace events are timed; at least as
105105 /// early as the creation of the sandbox being traced.
106- #[ allow( dead_code) ]
107106 pub epoch : std:: time:: Instant ,
108107 /// The frequency of the timestamp counter.
109- #[ allow( dead_code) ]
110- pub tsc_freq : u64 ,
108+ pub tsc_freq : Option < u64 > ,
111109 /// The epoch at which the guest started, if it has started.
112110 /// This is used to calculate the time spent in the guest relative to the
113- /// time of the host.
114- #[ allow( dead_code) ]
111+ /// time when the host started.
115112 pub guest_start_epoch : Option < std:: time:: Instant > ,
116- /// The start guest time, in TSC cycles, for the current guest.
117- #[ allow( dead_code) ]
113+ /// The start guest time, in TSC cycles, for the current guest has a double purpose.
114+ /// This field is used in two ways:
115+ /// 1. It contains the TSC value recorded on the host when the guest started.
116+ /// This is used to calculate the TSC frequency which is the same on the host and guest.
117+ /// The TSC frequency is used to convert TSC values to timestamps in the trace.
118+ /// **NOTE**: This is only used until the TSC frequency is calculated, when the first
119+ /// records are received.
120+ /// 2. To store the TSC value at recorded on the guest when the guest started (first record
121+ /// received)
122+ /// This is used to calculate the records timestamps relative to when guest started.
118123 pub guest_start_tsc : Option < u64 > ,
119124 /// The file to which the trace is being written
120125 #[ allow( dead_code) ]
@@ -139,8 +144,17 @@ impl TraceInfo {
139144 ) -> crate :: Result < Self > {
140145 let mut path = std:: env:: current_dir ( ) ?;
141146 path. push ( "trace" ) ;
147+
148+ // create directory if it does not exist
149+ if !path. exists ( ) {
150+ std:: fs:: create_dir ( & path) ?;
151+ }
142152 path. push ( uuid:: Uuid :: new_v4 ( ) . to_string ( ) ) ;
143153 path. set_extension ( "trace" ) ;
154+
155+ log:: info!( "Creating trace file at: {}" , path. display( ) ) ;
156+ println ! ( "Creating trace file at: {}" , path. display( ) ) ;
157+
144158 #[ cfg( feature = "unwind_guest" ) ]
145159 let hash = unwind_module. hash ( ) ;
146160 #[ cfg( feature = "unwind_guest" ) ]
@@ -150,11 +164,17 @@ impl TraceInfo {
150164 let cache = framehop:: x86_64:: CacheX86_64 :: new ( ) ;
151165 ( unwinder, Arc :: new ( Mutex :: new ( cache) ) )
152166 } ;
153- let tsc_freq = Self :: calculate_tsc_freq ( ) ?;
167+ if !hyperlight_guest_tracing:: invariant_tsc:: has_invariant_tsc ( ) {
168+ // If the platform does not support invariant TSC, warn the user.
169+ // On Azure nested virtualization, the TSC invariant bit is not correctly reported, this is a known issue.
170+ log:: warn!(
171+ "Invariant TSC is not supported on this platform, trace timestamps may be inaccurate"
172+ ) ;
173+ }
154174
155175 let ret = Self {
156176 epoch : std:: time:: Instant :: now ( ) ,
157- tsc_freq,
177+ tsc_freq : None ,
158178 guest_start_epoch : None ,
159179 guest_start_tsc : None ,
160180 file : Arc :: new ( Mutex :: new ( std:: fs:: File :: create_new ( path) ?) ) ,
@@ -173,22 +193,40 @@ impl TraceInfo {
173193 Ok ( ret)
174194 }
175195
176- /// Calculate the TSC frequency based on the RDTSC instruction.
177- fn calculate_tsc_freq ( ) -> crate :: Result < u64 > {
178- if !hyperlight_guest_tracing:: invariant_tsc:: has_invariant_tsc ( ) {
179- return Err ( crate :: new_error!(
180- "Invariant TSC is not supported on this platform"
181- ) ) ;
182- }
183- let start = hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ;
184- let start_time = std:: time:: Instant :: now ( ) ;
185- // Sleep for 1 second to get a good sample
186- std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) ;
187- let end = hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ;
196+ /// Calculate the TSC frequency based on the RDTSC instruction on the host.
197+ fn calculate_tsc_freq ( & mut self ) -> crate :: Result < ( ) > {
198+ let ( start, start_time) = match (
199+ self . guest_start_tsc . as_ref ( ) ,
200+ self . guest_start_epoch . as_ref ( ) ,
201+ ) {
202+ ( Some ( start) , Some ( start_time) ) => ( * start, * start_time) ,
203+ _ => {
204+ // If the guest start TSC and time are not set, we use the current time and TSC.
205+ // This is not ideal, but it allows us to calculate the TSC frequency without
206+ // failing.
207+ // This is a fallback mechanism to ensure that we can still calculate, however it
208+ // should be noted that this may lead to inaccuracies in the TSC frequency.
209+ // The start time should be already set before running the guest for each sandbox.
210+ log:: error!(
211+ "Guest start TSC and time are not set. Calculating TSC frequency will use current time and TSC."
212+ ) ;
213+ (
214+ hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ,
215+ std:: time:: Instant :: now ( ) ,
216+ )
217+ }
218+ } ;
219+
188220 let end_time = std:: time:: Instant :: now ( ) ;
221+ let end = hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ;
222+
189223 let elapsed = end_time. duration_since ( start_time) . as_secs_f64 ( ) ;
224+ let tsc_freq = ( ( end - start) as f64 / elapsed) as u64 ;
225+
226+ log:: info!( "Calculated TSC frequency: {} Hz" , tsc_freq) ;
227+ self . tsc_freq = Some ( tsc_freq) ;
190228
191- Ok ( ( ( end - start ) as f64 / elapsed ) as u64 )
229+ Ok ( ( ) )
192230 }
193231}
194232
0 commit comments