extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicU64, Ordering};
use hyperlight_common::flatbuffer_wrappers::guest_trace_data::{
EventsBatchEncoder, EventsEncoder, GuestEvent,
};
use hyperlight_common::outb::OutBAction;
use tracing_core::Event;
use tracing_core::span::{Attributes, Id, Record};
use crate::invariant_tsc;
use crate::visitor::FieldsVisitor;
pub struct TraceBatchInfo {
pub serialized_data: Vec<u8>,
}
pub(crate) struct GuestState {
encoder: EventsBatchEncoder,
next_id: AtomicU64,
stack: Vec<u64>,
}
const ENCODER_CAPACITY: usize = 4096;
const ACTIVE_SPANS_CAPACITY: usize = 64;
fn send_to_host(data: &[u8]) {
unsafe {
core::arch::asm!("out dx, al",
in("dx") OutBAction::TraceBatch as u16,
in("al") 0u8,
in("r8") OutBAction::TraceBatch as u64,
in("r9") data.as_ptr() as u64,
in("r10") data.len() as u64,
);
}
}
impl GuestState {
pub(crate) fn new(guest_start_tsc: u64) -> Self {
let mut encoder = EventsBatchEncoder::new(ENCODER_CAPACITY, send_to_host);
encoder.encode(&GuestEvent::GuestStart {
tsc: guest_start_tsc,
});
Self {
encoder,
next_id: AtomicU64::new(1),
stack: Vec::with_capacity(ACTIVE_SPANS_CAPACITY),
}
}
pub(crate) fn alloc_id(&self) -> (u64, Id) {
let n = self.next_id.load(Ordering::Relaxed);
self.next_id.store(n + 1, Ordering::Relaxed);
(n, Id::from_u64(n))
}
pub(crate) fn flush(&mut self) {
self.end_trace();
self.encoder.flush();
}
pub(crate) fn new_call(&mut self, start_tsc: u64) {
self.encoder.reset();
self.encoder
.encode(&GuestEvent::GuestStart { tsc: start_tsc });
}
pub(crate) fn reset(&mut self) {
self.encoder.reset();
}
pub(crate) fn end_trace(&mut self) {
while let Some(id) = self.stack.pop() {
let event = GuestEvent::CloseSpan {
id,
tsc: invariant_tsc::read_tsc(),
};
self.encoder.encode(&event);
}
}
pub(crate) fn serialized_data(&self) -> Option<(u64, u64)> {
let data = self.encoder.finish();
if data.is_empty() {
None
} else {
Some((data.as_ptr() as u64, data.len() as u64))
}
}
pub(crate) fn new_span(&mut self, attrs: &Attributes) -> Id {
let (idn, id) = self.alloc_id();
let md = attrs.metadata();
let name = String::from(md.name());
let target = String::from(md.target());
let mut fields = Vec::new();
attrs.record(&mut FieldsVisitor { out: &mut fields });
let parent_id = self.stack.last().copied();
let event = GuestEvent::OpenSpan {
id: idn,
parent_id,
name,
target,
tsc: invariant_tsc::read_tsc(),
fields,
};
self.encoder.encode(&event);
id
}
pub(crate) fn event(&mut self, event: &Event<'_>) {
let stack = &mut self.stack;
let parent_id = stack.last().copied().unwrap_or(0);
let md = event.metadata();
let name = String::from(md.name());
let mut fields = Vec::new();
event.record(&mut FieldsVisitor { out: &mut fields });
let event = GuestEvent::LogEvent {
parent_id,
name,
tsc: invariant_tsc::read_tsc(),
fields,
};
self.encoder.encode(&event);
}
pub(crate) fn record(&mut self, s_id: &Id, values: &Record<'_>) {
let mut v = Vec::new();
values.record(&mut FieldsVisitor { out: &mut v });
let event = GuestEvent::EditSpan {
id: s_id.into_u64(),
fields: v,
};
self.encoder.encode(&event);
}
pub(crate) fn enter(&mut self, id: &Id) {
let st = &mut self.stack;
st.push(id.into_u64());
}
pub(crate) fn exit(&mut self, _id: &Id) {
let st = &mut self.stack;
let _ = st.pop();
}
pub(crate) fn try_close(&mut self, id: Id) -> bool {
let event = GuestEvent::CloseSpan {
id: id.into_u64(),
tsc: invariant_tsc::read_tsc(),
};
self.encoder.encode(&event);
true
}
}