@@ -137,17 +137,55 @@ pub struct GuestEvent<const N: usize, const FK: usize, const FV: usize, const F:
137137mod trace {
138138 extern crate alloc;
139139 use alloc:: sync:: { Arc , Weak } ;
140- use core:: sync:: atomic:: AtomicU64 ;
140+ use core:: fmt:: Debug ;
141+ use core:: sync:: atomic:: { AtomicU64 , Ordering } ;
142+ use tracing_core:: field:: { Field , Visit } ;
141143 use tracing_core:: span:: { Attributes , Id , Record } ;
142144 use tracing_core:: subscriber:: Subscriber ;
143145 use tracing_core:: { Event , Metadata } ;
144146 use spin:: Mutex ;
145147
146148 use super :: * ;
149+ use crate :: invariant_tsc;
147150
148151 /// Weak reference to the guest state so we can manually trigger flush to host
149152 static GUEST_STATE : spin:: Once < Weak < Mutex < GuestState > > > = spin:: Once :: new ( ) ;
150153
154+ /// Visitor implementation to collect fields into a vector of key-value pairs
155+ struct FieldsVisitor < ' a , const FK : usize , const FV : usize , const F : usize > {
156+ out : & ' a mut hl:: Vec < ( hl:: String < FK > , hl:: String < FV > ) , F > ,
157+ }
158+
159+ impl < ' a , const FK : usize , const FV : usize , const F : usize > Visit for FieldsVisitor < ' a , FK , FV , F > {
160+ fn record_bytes ( & mut self , field : & Field , value : & [ u8 ] ) {
161+ let mut k = hl:: String :: < FK > :: new ( ) ;
162+ let mut val = hl:: String :: < FV > :: new ( ) ;
163+ // Shorten key and value if they are bigger than the space allocated
164+ let _ = k. push_str ( & field. name ( ) [ ..usize:: min ( field. name ( ) . len ( ) , k. capacity ( ) ) ] ) ;
165+ let _ = val
166+ . push_str ( & alloc:: format!( "{value:?}" ) [ ..usize:: min ( value. len ( ) , val. capacity ( ) ) ] ) ;
167+ let _ = self . out . push ( ( k, val) ) ;
168+ }
169+ fn record_str ( & mut self , f : & Field , v : & str ) {
170+ let mut k = heapless:: String :: < FK > :: new ( ) ;
171+ let mut val = heapless:: String :: < FV > :: new ( ) ;
172+ // Shorten key and value if they are bigger than the space allocated
173+ let _ = k. push_str ( & f. name ( ) [ ..usize:: min ( f. name ( ) . len ( ) , k. capacity ( ) ) ] ) ;
174+ let _ = val. push_str ( & v[ ..usize:: min ( v. len ( ) , val. capacity ( ) ) ] ) ;
175+ let _ = self . out . push ( ( k, val) ) ;
176+ }
177+ fn record_debug ( & mut self , f : & Field , v : & dyn Debug ) {
178+ use heapless:: String ;
179+ let mut k = String :: < FK > :: new ( ) ;
180+ let mut val = String :: < FV > :: new ( ) ;
181+ // Shorten key and value if they are bigger than the space allocated
182+ let _ = k. push_str ( & f. name ( ) [ ..usize:: min ( f. name ( ) . len ( ) , k. capacity ( ) ) ] ) ;
183+ let v = alloc:: format!( "{v:?}" ) ;
184+ let _ = val. push_str ( & v[ ..usize:: min ( v. len ( ) , val. capacity ( ) ) ] ) ;
185+ let _ = self . out . push ( ( k, val) ) ;
186+ }
187+ }
188+
151189 /// Helper type to define the guest state with the configured constants
152190 pub type GuestState = TraceState <
153191 MAX_NO_OF_SPANS ,
@@ -198,35 +236,130 @@ mod trace {
198236 }
199237 }
200238
239+ fn alloc_id ( & self ) -> ( u64 , Id ) {
240+ let n = self . next_id . load ( Ordering :: Relaxed ) ;
241+ self . next_id . store ( n + 1 , Ordering :: Relaxed ) ;
242+
243+ ( n, Id :: from_u64 ( n) )
244+ }
245+
246+ /// Triggers a VM exit to flush the current spans to the host.
247+ /// This also clears the internal state to start fresh.
248+ fn send_to_host ( & mut self ) {
249+ }
250+
201251 /// Create a new span and push it on the stack
202252 pub fn new_span ( & mut self , attrs : & Attributes ) -> Id {
203- unimplemented ! ( )
253+ let ( idn, id) = self . alloc_id ( ) ;
254+
255+ let md = attrs. metadata ( ) ;
256+ let mut name = hl:: String :: < N > :: new ( ) ;
257+ let mut target = hl:: String :: < T > :: new ( ) ;
258+ // Shorten name and target if they are bigger than the space allocated
259+ let _ = name. push_str ( & md. name ( ) [ ..usize:: min ( md. name ( ) . len ( ) , name. capacity ( ) ) ] ) ;
260+ let _ =
261+ target. push_str ( & md. target ( ) [ ..usize:: min ( md. target ( ) . len ( ) , target. capacity ( ) ) ] ) ;
262+
263+ // Visit fields to collect them
264+ let mut fields = hl:: Vec :: new ( ) ;
265+ attrs. record ( & mut FieldsVisitor :: < FK , FV , F > { out : & mut fields } ) ;
266+
267+ // Find parent from current stack top (if any)
268+ let parent_id = self . stack . last ( ) . copied ( ) ;
269+
270+ let span = GuestSpan :: < EV , N , T , FK , FV , F > {
271+ id : idn,
272+ parent_id,
273+ level : ( * md. level ( ) ) . into ( ) ,
274+ name,
275+ target,
276+ start_tsc : invariant_tsc:: read_tsc ( ) ,
277+ end_tsc : None ,
278+ fields,
279+ events : hl:: Vec :: new ( ) ,
280+ } ;
281+
282+ let spans = & mut self . spans ;
283+ // Should never fail because we flush when full
284+ let _ = spans. push ( span) ;
285+
286+ // In case the spans Vec is full, we need to report them to the host
287+ if spans. len ( ) == spans. capacity ( ) {
288+ self . send_to_host ( ) ;
289+ }
290+
291+ id
204292 }
205293
206294 /// Record an event in the current span (top of the stack)
207295 pub fn event ( & mut self , event : & Event < ' _ > ) {
208- unimplemented ! ( )
296+ let stack = & mut self . stack ;
297+ let parent_id = stack. last ( ) . copied ( ) . unwrap_or ( 0 ) ;
298+
299+ let md = event. metadata ( ) ;
300+ let mut name = hl:: String :: < N > :: new ( ) ;
301+ // Shorten name and target if they are bigger than the space allocated
302+ let _ = name. push_str ( & md. name ( ) [ ..usize:: min ( md. name ( ) . len ( ) , name. capacity ( ) ) ] ) ;
303+
304+ let mut fields = hl:: Vec :: new ( ) ;
305+ event. record ( & mut FieldsVisitor :: < FK , FV , F > { out : & mut fields } ) ;
306+
307+ let ev = GuestEvent {
308+ level : ( * md. level ( ) ) . into ( ) ,
309+ name,
310+ tsc : invariant_tsc:: read_tsc ( ) ,
311+ fields,
312+ } ;
313+
314+ let spans = & mut self . spans ;
315+ // Maybe panic is not the best option here, but if we have an event
316+ // for a span that does not exist, something is very wrong.
317+ let span = spans
318+ . iter_mut ( )
319+ . find ( |s| s. id == parent_id)
320+ . expect ( "There should always be a span" ) ;
321+
322+ // Should never fail because we flush when full
323+ let _ = span. events . push ( ev) ;
324+
325+ // Flush buffer to host if full
326+ if span. events . len ( ) >= span. events . capacity ( ) {
327+ self . send_to_host ( ) ;
328+ }
209329 }
210330
211331 /// Record new values for an existing span
212332 fn record ( & mut self , id : & Id , values : & Record < ' _ > ) {
213- unimplemented ! ( )
333+ let spans = & mut self . spans ;
334+ if let Some ( s) = spans. iter_mut ( ) . find ( |s| s. id == id. into_u64 ( ) ) {
335+ let mut v = hl:: Vec :: new ( ) ;
336+ values. record ( & mut FieldsVisitor :: < FK , FV , F > { out : & mut v } ) ;
337+ s. fields . extend ( v) ;
338+ }
214339 }
215340
216341 /// Enter a span (push it on the stack)
217342 fn enter ( & mut self , id : & Id ) {
218- unimplemented ! ( )
343+ let st = & mut self . stack ;
344+ let _ = st. push ( id. into_u64 ( ) ) ;
219345 }
220346
221347 /// Exit a span (pop it from the stack)
222348 fn exit ( & mut self , _id : & Id ) {
223- unimplemented ! ( )
349+ let st = & mut self . stack ;
350+ let _ = st. pop ( ) ;
224351 }
225352
226353 /// Try to close a span by ID, returning true if successful
227354 /// Records the end timestamp for the span.
228355 fn try_close ( & mut self , id : Id ) -> bool {
229- unimplemented ! ( )
356+ let spans = & mut self . spans ;
357+ if let Some ( s) = spans. iter_mut ( ) . find ( |s| s. id == id. into_u64 ( ) ) {
358+ s. end_tsc = Some ( invariant_tsc:: read_tsc ( ) ) ;
359+ true
360+ } else {
361+ false
362+ }
230363 }
231364 }
232365
0 commit comments