@@ -16,7 +16,7 @@ limitations under the License.
1616#![ no_std]
1717use heapless as hl;
1818#[ cfg( feature = "trace" ) ]
19- pub use trace:: init_guest_tracing;
19+ pub use trace:: { TraceBatchInfo , end_trace , guest_trace_info , init_guest_tracing} ;
2020
2121/// Module for checking invariant TSC support and reading the timestamp counter
2222pub mod invariant_tsc {
@@ -139,18 +139,27 @@ mod trace {
139139 use alloc:: sync:: { Arc , Weak } ;
140140 use core:: fmt:: Debug ;
141141 use core:: sync:: atomic:: { AtomicU64 , Ordering } ;
142+
143+ use hyperlight_common:: outb:: OutBAction ;
144+ use spin:: Mutex ;
142145 use tracing_core:: field:: { Field , Visit } ;
143146 use tracing_core:: span:: { Attributes , Id , Record } ;
144147 use tracing_core:: subscriber:: Subscriber ;
145148 use tracing_core:: { Event , Metadata } ;
146- use spin:: Mutex ;
147149
148150 use super :: * ;
149151 use crate :: invariant_tsc;
150152
151153 /// Weak reference to the guest state so we can manually trigger flush to host
152154 static GUEST_STATE : spin:: Once < Weak < Mutex < GuestState > > > = spin:: Once :: new ( ) ;
153155
156+ pub struct TraceBatchInfo {
157+ /// The timestamp counter at the start of the guest execution.
158+ pub guest_start_tsc : u64 ,
159+ /// Pointer to the spans in the guest memory.
160+ pub spans_ptr : u64 ,
161+ }
162+
154163 /// Visitor implementation to collect fields into a vector of key-value pairs
155164 struct FieldsVisitor < ' a , const FK : usize , const FV : usize , const F : usize > {
156165 out : & ' a mut hl:: Vec < ( hl:: String < FK > , hl:: String < FV > ) , F > ,
@@ -207,6 +216,8 @@ mod trace {
207216 const FV : usize ,
208217 const F : usize ,
209218 > {
219+ /// Whether we need to cleanup the state on next access
220+ cleanup_needed : bool ,
210221 /// The timestamp counter at the start of the guest execution.
211222 guest_start_tsc : u64 ,
212223 /// Next span ID to allocate
@@ -229,6 +240,7 @@ mod trace {
229240 {
230241 fn new ( guest_start_tsc : u64 ) -> Self {
231242 Self {
243+ cleanup_needed : false ,
232244 guest_start_tsc,
233245 next_id : AtomicU64 :: new ( 1 ) ,
234246 spans : hl:: Vec :: new ( ) ,
@@ -243,13 +255,91 @@ mod trace {
243255 ( n, Id :: from_u64 ( n) )
244256 }
245257
258+ /// Cleanup internal state by removing closed spans and events
259+ /// This ensures that after a VM exit, we keep the spans that
260+ /// are still active (in the stack) and remove all other spans and events.
261+ fn clean ( & mut self ) {
262+ // used for computing the spans that need to be removed
263+ let mut ids: hl:: Vec < u64 , SP > = self . spans . iter ( ) . map ( |s| s. id ) . collect ( ) ;
264+
265+ for id in self . stack . iter ( ) {
266+ let position = ids. iter ( ) . position ( |s| * s == * id) . unwrap ( ) ;
267+ // remove the span id that is contained in the stack
268+ ids. remove ( position) ;
269+ }
270+
271+ // Remove the spans with the remaining ids
272+ for id in ids. into_iter ( ) {
273+ let spans = & mut self . spans ;
274+ let position = spans. iter ( ) . position ( |s| s. id == id) . unwrap ( ) ;
275+ spans. remove ( position) ;
276+ }
277+
278+ // Remove the events from the remaining spans
279+ for s in self . spans . iter_mut ( ) {
280+ s. events . clear ( ) ;
281+ }
282+ }
283+
284+ #[ inline( always) ]
285+ fn verify_and_clean ( & mut self ) {
286+ if self . cleanup_needed {
287+ self . clean ( ) ;
288+ self . cleanup_needed = false ;
289+ }
290+ }
291+
246292 /// Triggers a VM exit to flush the current spans to the host.
247293 /// This also clears the internal state to start fresh.
248294 fn send_to_host ( & mut self ) {
295+ let guest_start_tsc = self . guest_start_tsc ;
296+ let spans_ptr = self . spans . as_ptr ( ) as u64 ;
297+
298+ unsafe {
299+ core:: arch:: asm!( "out dx, al" ,
300+ // Port value for tracing
301+ in( "dx" ) OutBAction :: TraceBatch as u16 ,
302+ // Additional magic number to identify the action
303+ in( "r8" ) OutBAction :: TraceBatch as u64 ,
304+ in( "r9" ) spans_ptr,
305+ in( "r10" ) guest_start_tsc,
306+ ) ;
307+ }
308+
309+ self . clean ( ) ;
310+ }
311+
312+ /// Closes the trace by ending all spans
313+ /// NOTE: This expects an outb call to send the spans to the host.
314+ fn end_trace ( & mut self ) {
315+ for span in self . spans . iter_mut ( ) {
316+ if span. end_tsc . is_none ( ) {
317+ span. end_tsc = Some ( invariant_tsc:: read_tsc ( ) ) ;
318+ }
319+ }
320+
321+ // Empty the stack
322+ while self . stack . pop ( ) . is_some ( ) {
323+ // Pop all remaining spans from the stack
324+ }
325+
326+ // Mark for clearing when re-entering the VM because we might
327+ // not enter on the same place as we exited (e.g. halt)
328+ self . cleanup_needed = true ;
329+ }
330+
331+ /// Returns information about the information needed by the host to read the spans.
332+ pub fn guest_trace_info ( & mut self ) -> TraceBatchInfo {
333+ self . cleanup_needed = true ;
334+ TraceBatchInfo {
335+ guest_start_tsc : self . guest_start_tsc ,
336+ spans_ptr : self . spans . as_ptr ( ) as u64 ,
337+ }
249338 }
250339
251340 /// Create a new span and push it on the stack
252341 pub fn new_span ( & mut self , attrs : & Attributes ) -> Id {
342+ self . verify_and_clean ( ) ;
253343 let ( idn, id) = self . alloc_id ( ) ;
254344
255345 let md = attrs. metadata ( ) ;
@@ -293,6 +383,7 @@ mod trace {
293383
294384 /// Record an event in the current span (top of the stack)
295385 pub fn event ( & mut self , event : & Event < ' _ > ) {
386+ self . verify_and_clean ( ) ;
296387 let stack = & mut self . stack ;
297388 let parent_id = stack. last ( ) . copied ( ) . unwrap_or ( 0 ) ;
298389
@@ -430,4 +521,29 @@ mod trace {
430521 // Set global dispatcher
431522 let _ = tracing_core:: dispatcher:: set_global_default ( tracing_core:: Dispatch :: new ( sub) ) ;
432523 }
524+
525+ /// Ends the current trace by ending all active spans in the
526+ /// internal state and storing the end timestamps.
527+ ///
528+ /// This expects an outb call to send the spans to the host.
529+ /// After calling this function, the internal state is marked
530+ /// for cleaning on the next access.
531+ pub fn end_trace ( ) {
532+ if let Some ( w) = GUEST_STATE . get ( ) {
533+ if let Some ( state) = w. upgrade ( ) {
534+ state. lock ( ) . end_trace ( ) ;
535+ }
536+ }
537+ }
538+
539+ /// Returns information about the current trace state needed by the host to read the spans.
540+ pub fn guest_trace_info ( ) -> Option < TraceBatchInfo > {
541+ let mut res = None ;
542+ if let Some ( w) = GUEST_STATE . get ( ) {
543+ if let Some ( state) = w. upgrade ( ) {
544+ res = Some ( state. lock ( ) . guest_trace_info ( ) ) ;
545+ }
546+ }
547+ res
548+ }
433549}
0 commit comments