@@ -87,7 +87,7 @@ pub type ExtraAllowedSyscall = i64;
8787/// Determine whether a suitable hypervisor is available to run
8888/// this sandbox.
8989///
90- // Returns a boolean indicating whether a suitable hypervisor is present.
90+ /// Returns a boolean indicating whether a suitable hypervisor is present.
9191#[ instrument( skip_all, parent = Span :: current( ) ) ]
9292pub fn is_hypervisor_present ( ) -> bool {
9393 hypervisor:: get_available_hypervisor ( ) . is_some ( )
@@ -100,18 +100,23 @@ pub fn is_hypervisor_present() -> bool {
100100pub ( crate ) struct TraceInfo {
101101 /// The epoch against which trace events are timed; at least as
102102 /// early as the creation of the sandbox being traced.
103- #[ allow( dead_code) ]
104103 pub epoch : std:: time:: Instant ,
105104 /// The frequency of the timestamp counter.
106- #[ allow( dead_code) ]
107- pub tsc_freq : u64 ,
105+ pub tsc_freq : Option < u64 > ,
108106 /// The epoch at which the guest started, if it has started.
109107 /// This is used to calculate the time spent in the guest relative to the
110- /// time of the host.
111- #[ allow( dead_code) ]
108+ /// time when the host started.
112109 pub guest_start_epoch : Option < std:: time:: Instant > ,
113- /// The start guest time, in TSC cycles, for the current guest.
114- #[ allow( dead_code) ]
110+ /// The start guest time, in TSC cycles, for the current guest has a double purpose.
111+ /// This field is used in two ways:
112+ /// 1. It contains the TSC value recorded on the host when the guest started.
113+ /// This is used to calculate the TSC frequency which is the same on the host and guest.
114+ /// The TSC frequency is used to convert TSC values to timestamps in the trace.
115+ /// **NOTE**: This is only used until the TSC frequency is calculated, when the first
116+ /// records are received.
117+ /// 2. To store the TSC value at recorded on the guest when the guest started (first record
118+ /// received)
119+ /// This is used to calculate the records timestamps relative to when guest started.
115120 pub guest_start_tsc : Option < u64 > ,
116121 /// The file to which the trace is being written
117122 #[ allow( dead_code) ]
@@ -136,8 +141,17 @@ impl TraceInfo {
136141 ) -> crate :: Result < Self > {
137142 let mut path = std:: env:: current_dir ( ) ?;
138143 path. push ( "trace" ) ;
144+
145+ // create directory if it does not exist
146+ if !path. exists ( ) {
147+ std:: fs:: create_dir ( & path) ?;
148+ }
139149 path. push ( uuid:: Uuid :: new_v4 ( ) . to_string ( ) ) ;
140150 path. set_extension ( "trace" ) ;
151+
152+ log:: info!( "Creating trace file at: {}" , path. display( ) ) ;
153+ println ! ( "Creating trace file at: {}" , path. display( ) ) ;
154+
141155 #[ cfg( feature = "unwind_guest" ) ]
142156 let hash = unwind_module. hash ( ) ;
143157 #[ cfg( feature = "unwind_guest" ) ]
@@ -147,11 +161,17 @@ impl TraceInfo {
147161 let cache = framehop:: x86_64:: CacheX86_64 :: new ( ) ;
148162 ( unwinder, Arc :: new ( Mutex :: new ( cache) ) )
149163 } ;
150- let tsc_freq = Self :: calculate_tsc_freq ( ) ?;
164+ if !hyperlight_guest_tracing:: invariant_tsc:: has_invariant_tsc ( ) {
165+ // If the platform does not support invariant TSC, warn the user.
166+ // On Azure nested virtualization, the TSC invariant bit is not correctly reported, this is a known issue.
167+ log:: warn!(
168+ "Invariant TSC is not supported on this platform, trace timestamps may be inaccurate"
169+ ) ;
170+ }
151171
152172 let ret = Self {
153173 epoch : std:: time:: Instant :: now ( ) ,
154- tsc_freq,
174+ tsc_freq : None ,
155175 guest_start_epoch : None ,
156176 guest_start_tsc : None ,
157177 file : Arc :: new ( Mutex :: new ( std:: fs:: File :: create_new ( path) ?) ) ,
@@ -170,22 +190,40 @@ impl TraceInfo {
170190 Ok ( ret)
171191 }
172192
173- /// Calculate the TSC frequency based on the RDTSC instruction.
174- fn calculate_tsc_freq ( ) -> crate :: Result < u64 > {
175- if !hyperlight_guest_tracing:: invariant_tsc:: has_invariant_tsc ( ) {
176- return Err ( crate :: new_error!(
177- "Invariant TSC is not supported on this platform"
178- ) ) ;
179- }
180- let start = hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ;
181- let start_time = std:: time:: Instant :: now ( ) ;
182- // Sleep for 1 second to get a good sample
183- std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) ;
184- let end = hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ;
193+ /// Calculate the TSC frequency based on the RDTSC instruction on the host.
194+ fn calculate_tsc_freq ( & mut self ) -> crate :: Result < ( ) > {
195+ let ( start, start_time) = match (
196+ self . guest_start_tsc . as_ref ( ) ,
197+ self . guest_start_epoch . as_ref ( ) ,
198+ ) {
199+ ( Some ( start) , Some ( start_time) ) => ( * start, * start_time) ,
200+ _ => {
201+ // If the guest start TSC and time are not set, we use the current time and TSC.
202+ // This is not ideal, but it allows us to calculate the TSC frequency without
203+ // failing.
204+ // This is a fallback mechanism to ensure that we can still calculate, however it
205+ // should be noted that this may lead to inaccuracies in the TSC frequency.
206+ // The start time should be already set before running the guest for each sandbox.
207+ log:: error!(
208+ "Guest start TSC and time are not set. Calculating TSC frequency will use current time and TSC."
209+ ) ;
210+ (
211+ hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ,
212+ std:: time:: Instant :: now ( ) ,
213+ )
214+ }
215+ } ;
216+
185217 let end_time = std:: time:: Instant :: now ( ) ;
218+ let end = hyperlight_guest_tracing:: invariant_tsc:: read_tsc ( ) ;
219+
186220 let elapsed = end_time. duration_since ( start_time) . as_secs_f64 ( ) ;
221+ let tsc_freq = ( ( end - start) as f64 / elapsed) as u64 ;
222+
223+ log:: info!( "Calculated TSC frequency: {} Hz" , tsc_freq) ;
224+ self . tsc_freq = Some ( tsc_freq) ;
187225
188- Ok ( ( ( end - start ) as f64 / elapsed ) as u64 )
226+ Ok ( ( ) )
189227 }
190228}
191229
0 commit comments