Skip to content

Commit 3d62622

Browse files
committed
[trace-guest] add span and event storage logic
Signed-off-by: Doru Blânzeanu <[email protected]>
1 parent 8fda8e8 commit 3d62622

File tree

1 file changed

+140
-7
lines changed
  • src/hyperlight_guest_tracing/src

1 file changed

+140
-7
lines changed

src/hyperlight_guest_tracing/src/lib.rs

Lines changed: 140 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,55 @@ pub struct GuestEvent<const N: usize, const FK: usize, const FV: usize, const F:
137137
mod 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

Comments
 (0)