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::{GuestEvent, GuestTraceData};
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 {
cleanup_needed: bool,
next_id: AtomicU64,
data: GuestTraceData,
stack: Vec<u64>,
}
const EVENTS_VEC_CAPACITY: usize = 30;
const MAX_NO_OF_SPANS: usize = EVENTS_VEC_CAPACITY / 2;
impl GuestState {
pub(crate) fn new(guest_start_tsc: u64) -> Self {
Self {
cleanup_needed: false,
next_id: AtomicU64::new(1),
data: GuestTraceData {
start_tsc: guest_start_tsc,
events: Vec::with_capacity(EVENTS_VEC_CAPACITY),
},
stack: Vec::with_capacity(MAX_NO_OF_SPANS),
}
}
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 fn clean(&mut self) {
self.data.events.clear();
}
#[inline(always)]
fn verify_and_clean(&mut self) {
if self.cleanup_needed {
self.clean();
self.cleanup_needed = false;
}
}
fn send_to_host(&mut self) {
let tb = self.guest_trace_info();
unsafe {
core::arch::asm!("out dx, al",
in("dx") OutBAction::TraceBatch as u16,
in("r8") OutBAction::TraceBatch as u64,
in("r9") tb.serialized_data.as_ptr() as u64,
in("r10") tb.serialized_data.len() as u64,
);
}
self.clean();
}
pub(crate) fn set_start_tsc(&mut self, guest_start_tsc: u64) {
self.data.start_tsc = guest_start_tsc;
}
pub(crate) fn end_trace(&mut self) {
while let Some(id) = self.stack.pop() {
let event = GuestEvent::CloseSpan {
id,
tsc: invariant_tsc::read_tsc(),
};
let events = &mut self.data.events;
events.push(event);
if events.len() >= EVENTS_VEC_CAPACITY {
self.send_to_host();
}
}
self.cleanup_needed = true;
}
pub(crate) fn guest_trace_info(&mut self) -> TraceBatchInfo {
let serialized_data: Vec<u8> = Vec::from(&self.data);
TraceBatchInfo { serialized_data }
}
pub(crate) fn new_span(&mut self, attrs: &Attributes) -> Id {
self.verify_and_clean();
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,
};
let events = &mut self.data.events;
events.push(event);
if events.len() >= EVENTS_VEC_CAPACITY {
self.send_to_host();
}
id
}
pub(crate) fn event(&mut self, event: &Event<'_>) {
self.verify_and_clean();
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 ev = GuestEvent::LogEvent {
parent_id,
name,
tsc: invariant_tsc::read_tsc(),
fields,
};
self.data.events.push(ev);
if self.data.events.len() >= EVENTS_VEC_CAPACITY {
self.send_to_host();
}
}
pub(crate) fn record(&mut self, s_id: &Id, values: &Record<'_>) {
let spans = &mut self.data.events;
if let Some(GuestEvent::OpenSpan { fields, .. }) = spans.iter_mut().find(|e| {
if let GuestEvent::OpenSpan { id, .. } = e {
*id == s_id.into_u64()
} else {
false
}
}) {
let mut v = Vec::new();
values.record(&mut FieldsVisitor { out: &mut v });
fields.extend(v);
}
}
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 events = &mut self.data.events;
let event = GuestEvent::CloseSpan {
id: id.into_u64(),
tsc: invariant_tsc::read_tsc(),
};
events.push(event);
if events.len() >= EVENTS_VEC_CAPACITY {
self.send_to_host();
}
true
}
}