diff --git a/Cargo.lock b/Cargo.lock index 9808b4292..d81ba9f24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1243,15 +1243,6 @@ dependencies = [ "zerocopy 0.8.27", ] -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -1268,17 +1259,6 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" -[[package]] -name = "heapless" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" -dependencies = [ - "hash32", - "serde", - "stable_deref_trait", -] - [[package]] name = "heck" version = "0.5.0" @@ -1473,7 +1453,6 @@ dependencies = [ name = "hyperlight-guest-tracing" version = "0.10.0" dependencies = [ - "heapless", "hyperlight-common", "spin 0.10.0", "tracing", diff --git a/Justfile b/Justfile index 9002159e5..b3cf9b64d 100644 --- a/Justfile +++ b/Justfile @@ -194,7 +194,8 @@ test-rust-crashdump target=default-target features="": # rust test for tracing test-rust-tracing target=default-target features="": # Run tests for the tracing guest and macro - {{ cargo-cmd }} test -p hyperlight-guest-tracing --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} + {{ cargo-cmd }} test -p hyperlight-guest-tracing -F trace --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} + {{ cargo-cmd }} test -p hyperlight-common -F trace_guest --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} # Build the tracing guest to ensure it builds with the tracing feature just build-rust-guests {{ target }} trace_guest diff --git a/src/hyperlight_common/src/flatbuffer_wrappers/guest_trace_data.rs b/src/hyperlight_common/src/flatbuffer_wrappers/guest_trace_data.rs new file mode 100644 index 000000000..0d21b3a2a --- /dev/null +++ b/src/hyperlight_common/src/flatbuffer_wrappers/guest_trace_data.rs @@ -0,0 +1,670 @@ +/* +Copyright 2025 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//! Guest trace data structures and (de)serialization logic. +//! This module defines the data structures used for tracing spans and events +//! within a guest environment, along with the logic for serializing and +//! deserializing these structures using FlatBuffers. +//! +//! Schema definitions can be found in `src/schema/guest_trace_data.fbs`. + +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +use anyhow::{Error, Result, anyhow}; +use flatbuffers::size_prefixed_root; + +use crate::flatbuffers::hyperlight::generated::{ + CloseSpanType as FbCloseSpanType, CloseSpanTypeArgs as FbCloseSpanTypeArgs, + GuestEventEnvelopeType as FbGuestEventEnvelopeType, + GuestEventEnvelopeTypeArgs as FbGuestEventEnvelopeTypeArgs, GuestEventType as FbGuestEventType, + GuestTraceDataType as FbGuestTraceDataType, GuestTraceDataTypeArgs as FbGuestTraceDataTypeArgs, + KeyValue as FbKeyValue, KeyValueArgs as FbKeyValueArgs, LogEventType as FbLogEventType, + LogEventTypeArgs as FbLogEventTypeArgs, OpenSpanType as FbOpenSpanType, + OpenSpanTypeArgs as FbOpenSpanTypeArgs, +}; + +/// Key-Value pair structure used in tracing spans/events +#[derive(Debug, Clone)] +pub struct KeyValue { + /// Key of the key-value pair + pub key: String, + /// Value of the key-value pair + pub value: String, +} + +impl From> for KeyValue { + fn from(value: FbKeyValue<'_>) -> Self { + let key = value.key().to_string(); + let value = value.value().to_string(); + + KeyValue { key, value } + } +} + +impl TryFrom<&[u8]> for KeyValue { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + let gld_gen = size_prefixed_root::(value) + .map_err(|e| anyhow!("Error while reading KeyValue: {:?}", e))?; + let key = gld_gen.key().to_string(); + let value = gld_gen.value().to_string(); + + Ok(KeyValue { key, value }) + } +} + +impl From<&KeyValue> for Vec { + fn from(value: &KeyValue) -> Self { + let mut builder = flatbuffers::FlatBufferBuilder::new(); + + let key_offset = builder.create_string(&value.key); + let value_offset = builder.create_string(&value.value); + + let kv_args = FbKeyValueArgs { + key: Some(key_offset), + value: Some(value_offset), + }; + + let kv_fb = FbKeyValue::create(&mut builder, &kv_args); + builder.finish_size_prefixed(kv_fb, None); + + builder.finished_data().to_vec() + } +} + +impl From for Vec { + fn from(value: KeyValue) -> Self { + Vec::from(&value) + } +} + +/// Enum representing different types of guest events for tracing +/// such as opening/closing spans and logging events. +#[derive(Debug)] +pub enum GuestEvent { + /// Event representing the opening of a new tracing span. + OpenSpan { + /// Unique identifier for the span. + /// This ID is used to correlate open and close events. + /// It should be unique within the context of a sandboxed guest execution. + id: u64, + /// Optional parent span ID, if this span is nested within another span. + parent_id: Option, + /// Name of the span. + name: String, + /// Target associated with the span. + target: String, + /// Timestamp Counter (TSC) value when the span was opened. + tsc: u64, + /// Additional key-value fields associated with the span. + fields: Vec, + }, + /// Event representing the closing of a tracing span. + CloseSpan { + /// Unique identifier for the span being closed. + id: u64, + /// Timestamp Counter (TSC) value when the span was closed. + tsc: u64, + }, + /// Event representing a log entry within a tracing span. + LogEvent { + /// Identifier of the parent span for this log event. + parent_id: u64, + /// Name of the log event. + name: String, + /// Timestamp Counter (TSC) value when the log event occurred. + tsc: u64, + /// Additional key-value fields associated with the log event. + fields: Vec, + }, +} + +/// Guest trace data structure containing a sequence of guest events +/// and the starting TSC (Timestamp Counter) for the guest. +#[derive(Debug)] +pub struct GuestTraceData { + /// The starting TSC value for the guest environment. + pub start_tsc: u64, + /// A vector of guest events recorded during execution. + pub events: Vec, +} + +impl TryFrom<&[u8]> for GuestTraceData { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + let gtd_gen = size_prefixed_root::(value) + .map_err(|e| anyhow!("Error while reading GuestTraceData: {:?}", e))?; + // Extract start_tsc + let start_tsc = gtd_gen.start_tsc(); + + // Extract events + let mut events = Vec::new(); + + // Iterate over each event in the FlatBuffer and convert to GuestEvent + if let Some(fb_events) = gtd_gen.events() { + for i in 0..fb_events.len() { + // Get the event envelope and determine its type + // to extract the specific event data from the union. + let envelope = fb_events.get(i); + let event_type = envelope.event_type(); + + // Match on the event type to extract the appropriate event data + let event = match event_type { + FbGuestEventType::OpenSpanType => { + // Extract OpenSpanType event data + let ost_fb = envelope + .event_as_open_span_type() + .ok_or_else(|| anyhow!("Failed to cast to OpenSpanType"))?; + + // Extract fields + let id = ost_fb.id(); + let parent = ost_fb.parent(); + let name = ost_fb.name().to_string(); + let target = ost_fb.target().to_string(); + let tsc = ost_fb.tsc(); + + // Extract key-value fields + let mut fields = Vec::new(); + if let Some(fb_fields) = ost_fb.fields() { + for j in 0..fb_fields.len() { + let kv: KeyValue = KeyValue::from(fb_fields.get(j)); + fields.push(kv); + } + } + + // Construct OpenSpan event + GuestEvent::OpenSpan { + id, + parent_id: parent, + name, + target, + tsc, + fields, + } + } + FbGuestEventType::CloseSpanType => { + // Extract CloseSpanType event data + let cst_fb = envelope + .event_as_close_span_type() + .ok_or_else(|| anyhow!("Failed to cast to CloseSpanType"))?; + // Extract fields + let id = cst_fb.id(); + let tsc = cst_fb.tsc(); + + // Construct CloseSpan event + GuestEvent::CloseSpan { id, tsc } + } + FbGuestEventType::LogEventType => { + // Extract LogEventType event data + let le_fb = envelope + .event_as_log_event_type() + .ok_or_else(|| anyhow!("Failed to cast to LogEventType"))?; + + // Extract fields + let parent_id = le_fb.parent_id(); + let name = le_fb.name().to_string(); + let tsc = le_fb.tsc(); + + // Extract key-value fields + let mut fields = Vec::new(); + if let Some(fb_fields) = le_fb.fields() { + for j in 0..fb_fields.len() { + let kv: KeyValue = KeyValue::from(fb_fields.get(j)); + fields.push(kv); + } + } + + // Construct LogEvent + GuestEvent::LogEvent { + parent_id, + name, + tsc, + fields, + } + } + _ => { + return Err(anyhow!("Unknown GuestEventType variant at index {}", i)); + } + }; + events.push(event); + } + } + Ok(GuestTraceData { start_tsc, events }) + } +} + +impl From<&GuestTraceData> for Vec { + fn from(value: &GuestTraceData) -> Self { + // Create a FlatBuffer builder + let mut builder = flatbuffers::FlatBufferBuilder::new(); + + // Serialize each GuestEvent into its corresponding FlatBuffer representation + let mut event_offsets = Vec::new(); + for event in &value.events { + // Serialize the event based on its type + let event_offset: flatbuffers::WIPOffset = match event { + GuestEvent::OpenSpan { + id, + parent_id, + name, + target, + tsc, + fields, + } => { + // Serialize strings + let name_offset = builder.create_string(name); + let target_offset = builder.create_string(target); + + // Serialize key-value fields + let mut field_offsets = Vec::new(); + for field in fields { + let field_offset: flatbuffers::WIPOffset = { + let key_offset = builder.create_string(&field.key); + let value_offset = builder.create_string(&field.value); + let kv_args = FbKeyValueArgs { + key: Some(key_offset), + value: Some(value_offset), + }; + FbKeyValue::create(&mut builder, &kv_args) + }; + field_offsets.push(field_offset); + } + + // Create fields vector + let fields_vector = if !field_offsets.is_empty() { + Some(builder.create_vector(&field_offsets)) + } else { + None + }; + + let ost_args = FbOpenSpanTypeArgs { + id: *id, + parent: *parent_id, + name: Some(name_offset), + target: Some(target_offset), + tsc: *tsc, + fields: fields_vector, + }; + + // Create the OpenSpanType FlatBuffer object + let ost_fb = FbOpenSpanType::create(&mut builder, &ost_args); + + // Create the GuestEventEnvelopeType + let guest_event_fb = FbGuestEventType::OpenSpanType; + let envelope_args = FbGuestEventEnvelopeTypeArgs { + event_type: guest_event_fb, + event: Some(ost_fb.as_union_value()), + }; + + // Create the envelope using the union value + FbGuestEventEnvelopeType::create(&mut builder, &envelope_args) + } + GuestEvent::CloseSpan { id, tsc } => { + // Create CloseSpanType FlatBuffer object + let cst_args = FbCloseSpanTypeArgs { id: *id, tsc: *tsc }; + let cst_fb = FbCloseSpanType::create(&mut builder, &cst_args); + + // Create the GuestEventEnvelopeType + let guest_event_fb = FbGuestEventType::CloseSpanType; + let envelope_args = FbGuestEventEnvelopeTypeArgs { + event_type: guest_event_fb, + event: Some(cst_fb.as_union_value()), + }; + // Create the envelope using the union value + FbGuestEventEnvelopeType::create(&mut builder, &envelope_args) + } + GuestEvent::LogEvent { + parent_id, + name, + tsc, + fields, + } => { + // Serialize strings + let name_offset = builder.create_string(name); + + // Serialize key-value fields + let mut field_offsets = Vec::new(); + for field in fields { + let field_offset: flatbuffers::WIPOffset = { + let key_offset = builder.create_string(&field.key); + let value_offset = builder.create_string(&field.value); + let kv_args = FbKeyValueArgs { + key: Some(key_offset), + value: Some(value_offset), + }; + FbKeyValue::create(&mut builder, &kv_args) + }; + field_offsets.push(field_offset); + } + + let fields_vector = if !field_offsets.is_empty() { + Some(builder.create_vector(&field_offsets)) + } else { + None + }; + + let le_args = FbLogEventTypeArgs { + parent_id: *parent_id, + name: Some(name_offset), + tsc: *tsc, + fields: fields_vector, + }; + + let le_fb = FbLogEventType::create(&mut builder, &le_args); + + // Create the GuestEventEnvelopeType + let guest_event_fb = FbGuestEventType::LogEventType; + let envelope_args = FbGuestEventEnvelopeTypeArgs { + event_type: guest_event_fb, + event: Some(le_fb.as_union_value()), + }; + // Create the envelope using the union value + FbGuestEventEnvelopeType::create(&mut builder, &envelope_args) + } + }; + + // Collect the event offset + event_offsets.push(event_offset); + } + + let events_vector = if !event_offsets.is_empty() { + Some(builder.create_vector(&event_offsets)) + } else { + None + }; + + let gtd_args = FbGuestTraceDataTypeArgs { + start_tsc: value.start_tsc, + events: events_vector, + }; + + // Create the GuestTraceDataType FlatBuffer object + let gtd_fb = FbGuestTraceDataType::create(&mut builder, >d_args); + builder.finish_size_prefixed(gtd_fb, None); + builder.finished_data().to_vec() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Utility function to check an original GuestTraceData against a deserialized one + fn check_fb_guest_trace_data(orig: &GuestTraceData, deserialized: &GuestTraceData) { + assert_eq!(orig.start_tsc, deserialized.start_tsc); + assert_eq!(orig.events.len(), deserialized.events.len()); + for (original, deserialized) in orig.events.iter().zip(deserialized.events.iter()) { + match (original, deserialized) { + ( + GuestEvent::OpenSpan { + id: oid, + parent_id: opid, + name: oname, + target: otarget, + tsc: otsc, + fields: ofields, + }, + GuestEvent::OpenSpan { + id: did, + parent_id: dpid, + name: dname, + target: dtarget, + tsc: dtsc, + fields: dfields, + }, + ) => { + assert_eq!(oid, did); + assert_eq!(opid, dpid); + assert_eq!(oname, dname); + assert_eq!(otarget, dtarget); + assert_eq!(otsc, dtsc); + assert_eq!(ofields.len(), dfields.len()); + for (o_field, d_field) in ofields.iter().zip(dfields.iter()) { + assert_eq!(o_field.key, d_field.key); + assert_eq!(o_field.value, d_field.value); + } + } + ( + GuestEvent::LogEvent { + parent_id: opid, + name: oname, + tsc: otsc, + fields: ofields, + }, + GuestEvent::LogEvent { + parent_id: dpid, + name: dname, + tsc: dtsc, + fields: dfields, + }, + ) => { + assert_eq!(opid, dpid); + assert_eq!(oname, dname); + assert_eq!(otsc, dtsc); + assert_eq!(ofields.len(), dfields.len()); + for (o_field, d_field) in ofields.iter().zip(dfields.iter()) { + assert_eq!(o_field.key, d_field.key); + assert_eq!(o_field.value, d_field.value); + } + } + ( + GuestEvent::CloseSpan { id: oid, tsc: otsc }, + GuestEvent::CloseSpan { id: did, tsc: dtsc }, + ) => { + assert_eq!(oid, did); + assert_eq!(otsc, dtsc); + } + _ => panic!("Mismatched event types"), + } + } + } + + #[test] + fn test_fb_key_value_serialization() { + let kv = KeyValue { + key: "test_key".to_string(), + value: "test_value".to_string(), + }; + + let serialized: Vec = Vec::from(&kv); + let deserialized: KeyValue = + KeyValue::try_from(serialized.as_slice()).expect("Deserialization failed"); + + assert_eq!(kv.key, deserialized.key); + assert_eq!(kv.value, deserialized.value); + } + + #[test] + fn test_fb_guest_trace_data_open_span_serialization() { + let kv1 = KeyValue { + key: "test_key1".to_string(), + value: "test_value1".to_string(), + }; + let kv2 = KeyValue { + key: "test_key1".to_string(), + value: "test_value2".to_string(), + }; + + let open_span = GuestEvent::OpenSpan { + id: 1, + parent_id: None, + name: "span_name".to_string(), + target: "span_target".to_string(), + tsc: 100, + fields: Vec::from([kv1, kv2]), + }; + + let guest_trace_data = GuestTraceData { + start_tsc: 50, + events: Vec::from([open_span]), + }; + + let serialized: Vec = Vec::from(&guest_trace_data); + let deserialized: GuestTraceData = + GuestTraceData::try_from(serialized.as_slice()).expect("Deserialization failed"); + + check_fb_guest_trace_data(&guest_trace_data, &deserialized); + } + + #[test] + fn test_fb_guest_trace_data_close_span_serialization() { + let close_span = GuestEvent::CloseSpan { id: 1, tsc: 200 }; + + let guest_trace_data = GuestTraceData { + start_tsc: 150, + events: Vec::from([close_span]), + }; + + let serialized: Vec = Vec::from(&guest_trace_data); + let deserialized: GuestTraceData = + GuestTraceData::try_from(serialized.as_slice()).expect("Deserialization failed"); + + check_fb_guest_trace_data(&guest_trace_data, &deserialized); + } + + #[test] + fn test_fb_guest_trace_data_log_event_serialization() { + let kv1 = KeyValue { + key: "log_key1".to_string(), + value: "log_value1".to_string(), + }; + let kv2 = KeyValue { + key: "log_key2".to_string(), + value: "log_value2".to_string(), + }; + + let log_event = GuestEvent::LogEvent { + parent_id: 2, + name: "log_name".to_string(), + tsc: 300, + fields: Vec::from([kv1, kv2]), + }; + + let guest_trace_data = GuestTraceData { + start_tsc: 250, + events: Vec::from([log_event]), + }; + + let serialized: Vec = Vec::from(&guest_trace_data); + let deserialized: GuestTraceData = + GuestTraceData::try_from(serialized.as_slice()).expect("Deserialization failed"); + + check_fb_guest_trace_data(&guest_trace_data, &deserialized); + } + + /// Test serialization and deserialization of GuestTraceData with multiple events + /// [OpenSpan, LogEvent, CloseSpan] + #[test] + fn test_fb_guest_trace_data_multiple_events_serialization_0() { + let kv1 = KeyValue { + key: "span_field1".to_string(), + value: "span_value1".to_string(), + }; + let kv2 = KeyValue { + key: "log_field1".to_string(), + value: "log_value1".to_string(), + }; + + let open_span = GuestEvent::OpenSpan { + id: 1, + parent_id: None, + name: "span_name".to_string(), + target: "span_target".to_string(), + tsc: 100, + fields: Vec::from([kv1]), + }; + + let log_event = GuestEvent::LogEvent { + parent_id: 1, + name: "log_name".to_string(), + tsc: 150, + fields: Vec::from([kv2]), + }; + + let close_span = GuestEvent::CloseSpan { id: 1, tsc: 200 }; + + let guest_trace_data = GuestTraceData { + start_tsc: 50, + events: Vec::from([open_span, log_event, close_span]), + }; + + let serialized: Vec = Vec::from(&guest_trace_data); + let deserialized: GuestTraceData = + GuestTraceData::try_from(serialized.as_slice()).expect("Deserialization failed"); + + check_fb_guest_trace_data(&guest_trace_data, &deserialized); + } + + /// Test serialization and deserialization of GuestTraceData with multiple events + /// [OpenSpan, LogEvent, OpenSpan, LogEvent, CloseSpan] + #[test] + fn test_fb_guest_trace_data_multiple_events_serialization_1() { + let kv1 = KeyValue { + key: "span_field1".to_string(), + value: "span_value1".to_string(), + }; + let kv2 = KeyValue { + key: "log_field1".to_string(), + value: "log_value1".to_string(), + }; + + let open_span1 = GuestEvent::OpenSpan { + id: 1, + parent_id: None, + name: "span_name_1".to_string(), + target: "span_target_1".to_string(), + tsc: 100, + fields: Vec::from([kv1]), + }; + let open_span2 = GuestEvent::OpenSpan { + id: 2, + parent_id: Some(1), + name: "span_name_2".to_string(), + target: "span_target_2".to_string(), + tsc: 1000, + fields: Vec::from([kv2.clone()]), + }; + + let log_event1 = GuestEvent::LogEvent { + parent_id: 1, + name: "log_name_1".to_string(), + tsc: 150, + fields: Vec::from([kv2.clone()]), + }; + let log_event2 = GuestEvent::LogEvent { + parent_id: 2, + name: "log_name".to_string(), + tsc: 1050, + fields: Vec::from([kv2]), + }; + + let close_span = GuestEvent::CloseSpan { id: 2, tsc: 2000 }; + + let guest_trace_data = GuestTraceData { + start_tsc: 50, + events: Vec::from([open_span1, log_event1, open_span2, log_event2, close_span]), + }; + + let serialized: Vec = Vec::from(&guest_trace_data); + let deserialized: GuestTraceData = + GuestTraceData::try_from(serialized.as_slice()).expect("Deserialization failed"); + + check_fb_guest_trace_data(&guest_trace_data, &deserialized); + } +} diff --git a/src/hyperlight_common/src/flatbuffer_wrappers/mod.rs b/src/hyperlight_common/src/flatbuffer_wrappers/mod.rs index 5e6edcf8c..57b320de4 100644 --- a/src/hyperlight_common/src/flatbuffer_wrappers/mod.rs +++ b/src/hyperlight_common/src/flatbuffer_wrappers/mod.rs @@ -22,6 +22,9 @@ pub mod guest_log_data; /// cbindgen:ignore pub mod guest_log_level; /// cbindgen:ignore +#[cfg(feature = "trace_guest")] +pub mod guest_trace_data; +/// cbindgen:ignore pub mod host_function_definition; /// cbindgen:ignore pub mod host_function_details; diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/close_span_type_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/close_span_type_generated.rs new file mode 100644 index 000000000..c88f61c63 --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/close_span_type_generated.rs @@ -0,0 +1,131 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +pub enum CloseSpanTypeOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct CloseSpanType<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for CloseSpanType<'a> { + type Inner = CloseSpanType<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> CloseSpanType<'a> { + pub const VT_ID: flatbuffers::VOffsetT = 4; + pub const VT_TSC: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + CloseSpanType { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CloseSpanTypeArgs, + ) -> flatbuffers::WIPOffset> { + let mut builder = CloseSpanTypeBuilder::new(_fbb); + builder.add_tsc(args.tsc); + builder.add_id(args.id); + builder.finish() + } + + #[inline] + pub fn id(&self) -> u64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(CloseSpanType::VT_ID, Some(0)).unwrap() } + } + #[inline] + pub fn tsc(&self) -> u64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(CloseSpanType::VT_TSC, Some(0)) + .unwrap() + } + } +} + +impl flatbuffers::Verifiable for CloseSpanType<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("id", Self::VT_ID, false)? + .visit_field::("tsc", Self::VT_TSC, false)? + .finish(); + Ok(()) + } +} +pub struct CloseSpanTypeArgs { + pub id: u64, + pub tsc: u64, +} +impl<'a> Default for CloseSpanTypeArgs { + #[inline] + fn default() -> Self { + CloseSpanTypeArgs { id: 0, tsc: 0 } + } +} + +pub struct CloseSpanTypeBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> CloseSpanTypeBuilder<'a, 'b, A> { + #[inline] + pub fn add_id(&mut self, id: u64) { + self.fbb_.push_slot::(CloseSpanType::VT_ID, id, 0); + } + #[inline] + pub fn add_tsc(&mut self, tsc: u64) { + self.fbb_.push_slot::(CloseSpanType::VT_TSC, tsc, 0); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> CloseSpanTypeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CloseSpanTypeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for CloseSpanType<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("CloseSpanType"); + ds.field("id", &self.id()); + ds.field("tsc", &self.tsc()); + ds.finish() + } +} diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_envelope_type_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_envelope_type_generated.rs new file mode 100644 index 000000000..e83e3ed9c --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_envelope_type_generated.rs @@ -0,0 +1,252 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +pub enum GuestEventEnvelopeTypeOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct GuestEventEnvelopeType<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for GuestEventEnvelopeType<'a> { + type Inner = GuestEventEnvelopeType<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> GuestEventEnvelopeType<'a> { + pub const VT_EVENT_TYPE: flatbuffers::VOffsetT = 4; + pub const VT_EVENT: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + GuestEventEnvelopeType { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args GuestEventEnvelopeTypeArgs, + ) -> flatbuffers::WIPOffset> { + let mut builder = GuestEventEnvelopeTypeBuilder::new(_fbb); + if let Some(x) = args.event { + builder.add_event(x); + } + builder.add_event_type(args.event_type); + builder.finish() + } + + #[inline] + pub fn event_type(&self) -> GuestEventType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::( + GuestEventEnvelopeType::VT_EVENT_TYPE, + Some(GuestEventType::NONE), + ) + .unwrap() + } + } + #[inline] + pub fn event(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + GuestEventEnvelopeType::VT_EVENT, + None, + ) + } + } + #[inline] + #[allow(non_snake_case)] + pub fn event_as_open_span_type(&self) -> Option> { + if self.event_type() == GuestEventType::OpenSpanType { + self.event().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { OpenSpanType::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn event_as_close_span_type(&self) -> Option> { + if self.event_type() == GuestEventType::CloseSpanType { + self.event().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CloseSpanType::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn event_as_log_event_type(&self) -> Option> { + if self.event_type() == GuestEventType::LogEventType { + self.event().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LogEventType::init_from_table(t) } + }) + } else { + None + } + } +} + +impl flatbuffers::Verifiable for GuestEventEnvelopeType<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_union::( + "event_type", + Self::VT_EVENT_TYPE, + "event", + Self::VT_EVENT, + false, + |key, v, pos| match key { + GuestEventType::OpenSpanType => v + .verify_union_variant::>( + "GuestEventType::OpenSpanType", + pos, + ), + GuestEventType::CloseSpanType => v + .verify_union_variant::>( + "GuestEventType::CloseSpanType", + pos, + ), + GuestEventType::LogEventType => v + .verify_union_variant::>( + "GuestEventType::LogEventType", + pos, + ), + _ => Ok(()), + }, + )? + .finish(); + Ok(()) + } +} +pub struct GuestEventEnvelopeTypeArgs { + pub event_type: GuestEventType, + pub event: Option>, +} +impl<'a> Default for GuestEventEnvelopeTypeArgs { + #[inline] + fn default() -> Self { + GuestEventEnvelopeTypeArgs { + event_type: GuestEventType::NONE, + event: None, + } + } +} + +pub struct GuestEventEnvelopeTypeBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> GuestEventEnvelopeTypeBuilder<'a, 'b, A> { + #[inline] + pub fn add_event_type(&mut self, event_type: GuestEventType) { + self.fbb_.push_slot::( + GuestEventEnvelopeType::VT_EVENT_TYPE, + event_type, + GuestEventType::NONE, + ); + } + #[inline] + pub fn add_event(&mut self, event: flatbuffers::WIPOffset) { + self.fbb_ + .push_slot_always::>(GuestEventEnvelopeType::VT_EVENT, event); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> GuestEventEnvelopeTypeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + GuestEventEnvelopeTypeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for GuestEventEnvelopeType<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("GuestEventEnvelopeType"); + ds.field("event_type", &self.event_type()); + match self.event_type() { + GuestEventType::OpenSpanType => { + if let Some(x) = self.event_as_open_span_type() { + ds.field("event", &x) + } else { + ds.field( + "event", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + GuestEventType::CloseSpanType => { + if let Some(x) = self.event_as_close_span_type() { + ds.field("event", &x) + } else { + ds.field( + "event", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + GuestEventType::LogEventType => { + if let Some(x) = self.event_as_log_event_type() { + ds.field("event", &x) + } else { + ds.field( + "event", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + _ => { + let x: Option<()> = None; + ds.field("event", &x) + } + }; + ds.finish() + } +} diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_generated.rs new file mode 100644 index 000000000..1d89bd9c7 --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_generated.rs @@ -0,0 +1,249 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +pub enum GuestEventOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct GuestEvent<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for GuestEvent<'a> { + type Inner = GuestEvent<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> GuestEvent<'a> { + pub const VT_EVENT_TYPE: flatbuffers::VOffsetT = 4; + pub const VT_EVENT: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + GuestEvent { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args GuestEventArgs, + ) -> flatbuffers::WIPOffset> { + let mut builder = GuestEventBuilder::new(_fbb); + if let Some(x) = args.event { + builder.add_event(x); + } + builder.add_event_type(args.event_type); + builder.finish() + } + + #[inline] + pub fn event_type(&self) -> GuestEventType { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(GuestEvent::VT_EVENT_TYPE, Some(GuestEventType::NONE)) + .unwrap() + } + } + #[inline] + pub fn event(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + GuestEvent::VT_EVENT, + None, + ) + } + } + #[inline] + #[allow(non_snake_case)] + pub fn event_as_open_span_type(&self) -> Option> { + if self.event_type() == GuestEventType::OpenSpanType { + self.event().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { OpenSpanType::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn event_as_close_span_type(&self) -> Option> { + if self.event_type() == GuestEventType::CloseSpanType { + self.event().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { CloseSpanType::init_from_table(t) } + }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn event_as_log_event_type(&self) -> Option> { + if self.event_type() == GuestEventType::LogEventType { + self.event().map(|t| { + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + unsafe { LogEventType::init_from_table(t) } + }) + } else { + None + } + } +} + +impl flatbuffers::Verifiable for GuestEvent<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_union::( + "event_type", + Self::VT_EVENT_TYPE, + "event", + Self::VT_EVENT, + false, + |key, v, pos| match key { + GuestEventType::OpenSpanType => v + .verify_union_variant::>( + "GuestEventType::OpenSpanType", + pos, + ), + GuestEventType::CloseSpanType => v + .verify_union_variant::>( + "GuestEventType::CloseSpanType", + pos, + ), + GuestEventType::LogEventType => v + .verify_union_variant::>( + "GuestEventType::LogEventType", + pos, + ), + _ => Ok(()), + }, + )? + .finish(); + Ok(()) + } +} +pub struct GuestEventArgs { + pub event_type: GuestEventType, + pub event: Option>, +} +impl<'a> Default for GuestEventArgs { + #[inline] + fn default() -> Self { + GuestEventArgs { + event_type: GuestEventType::NONE, + event: None, + } + } +} + +pub struct GuestEventBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> GuestEventBuilder<'a, 'b, A> { + #[inline] + pub fn add_event_type(&mut self, event_type: GuestEventType) { + self.fbb_.push_slot::( + GuestEvent::VT_EVENT_TYPE, + event_type, + GuestEventType::NONE, + ); + } + #[inline] + pub fn add_event(&mut self, event: flatbuffers::WIPOffset) { + self.fbb_ + .push_slot_always::>(GuestEvent::VT_EVENT, event); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> GuestEventBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + GuestEventBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for GuestEvent<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("GuestEvent"); + ds.field("event_type", &self.event_type()); + match self.event_type() { + GuestEventType::OpenSpanType => { + if let Some(x) = self.event_as_open_span_type() { + ds.field("event", &x) + } else { + ds.field( + "event", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + GuestEventType::CloseSpanType => { + if let Some(x) = self.event_as_close_span_type() { + ds.field("event", &x) + } else { + ds.field( + "event", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + GuestEventType::LogEventType => { + if let Some(x) = self.event_as_log_event_type() { + ds.field("event", &x) + } else { + ds.field( + "event", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + _ => { + let x: Option<()> = None; + ds.field("event", &x) + } + }; + ds.finish() + } +} diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_type_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_type_generated.rs new file mode 100644 index 000000000..59696474f --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_event_type_generated.rs @@ -0,0 +1,118 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] +pub const ENUM_MIN_GUEST_EVENT_TYPE: u8 = 0; +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] +pub const ENUM_MAX_GUEST_EVENT_TYPE: u8 = 3; +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_GUEST_EVENT_TYPE: [GuestEventType; 4] = [ + GuestEventType::NONE, + GuestEventType::OpenSpanType, + GuestEventType::CloseSpanType, + GuestEventType::LogEventType, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct GuestEventType(pub u8); +#[allow(non_upper_case_globals)] +impl GuestEventType { + pub const NONE: Self = Self(0); + pub const OpenSpanType: Self = Self(1); + pub const CloseSpanType: Self = Self(2); + pub const LogEventType: Self = Self(3); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 3; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::OpenSpanType, + Self::CloseSpanType, + Self::LogEventType, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::OpenSpanType => Some("OpenSpanType"), + Self::CloseSpanType => Some("CloseSpanType"), + Self::LogEventType => Some("LogEventType"), + _ => None, + } + } +} +impl core::fmt::Debug for GuestEventType { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> flatbuffers::Follow<'a> for GuestEventType { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl flatbuffers::Push for GuestEventType { + type Output = GuestEventType; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { + flatbuffers::emplace_scalar::(dst, self.0); + } + } +} + +impl flatbuffers::EndianScalar for GuestEventType { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> flatbuffers::Verifiable for GuestEventType { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + u8::run_verifier(v, pos) + } +} + +impl flatbuffers::SimpleToVerifyInSlice for GuestEventType {} +pub struct GuestEventTypeUnionTableOffset {} diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_trace_data_type_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_trace_data_type_generated.rs new file mode 100644 index 000000000..2db1b3f32 --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/guest_trace_data_type_generated.rs @@ -0,0 +1,156 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +pub enum GuestTraceDataTypeOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct GuestTraceDataType<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for GuestTraceDataType<'a> { + type Inner = GuestTraceDataType<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> GuestTraceDataType<'a> { + pub const VT_START_TSC: flatbuffers::VOffsetT = 4; + pub const VT_EVENTS: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + GuestTraceDataType { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args GuestTraceDataTypeArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = GuestTraceDataTypeBuilder::new(_fbb); + builder.add_start_tsc(args.start_tsc); + if let Some(x) = args.events { + builder.add_events(x); + } + builder.finish() + } + + #[inline] + pub fn start_tsc(&self) -> u64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(GuestTraceDataType::VT_START_TSC, Some(0)) + .unwrap() + } + } + #[inline] + pub fn events( + &self, + ) -> Option>>> + { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(GuestTraceDataType::VT_EVENTS, None) + } + } +} + +impl flatbuffers::Verifiable for GuestTraceDataType<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("start_tsc", Self::VT_START_TSC, false)? + .visit_field::>, + >>("events", Self::VT_EVENTS, false)? + .finish(); + Ok(()) + } +} +pub struct GuestTraceDataTypeArgs<'a> { + pub start_tsc: u64, + pub events: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, +} +impl<'a> Default for GuestTraceDataTypeArgs<'a> { + #[inline] + fn default() -> Self { + GuestTraceDataTypeArgs { + start_tsc: 0, + events: None, + } + } +} + +pub struct GuestTraceDataTypeBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> GuestTraceDataTypeBuilder<'a, 'b, A> { + #[inline] + pub fn add_start_tsc(&mut self, start_tsc: u64) { + self.fbb_ + .push_slot::(GuestTraceDataType::VT_START_TSC, start_tsc, 0); + } + #[inline] + pub fn add_events( + &mut self, + events: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(GuestTraceDataType::VT_EVENTS, events); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> GuestTraceDataTypeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + GuestTraceDataTypeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for GuestTraceDataType<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("GuestTraceDataType"); + ds.field("start_tsc", &self.start_tsc()); + ds.field("events", &self.events()); + ds.finish() + } +} diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/key_value_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/key_value_generated.rs new file mode 100644 index 000000000..06516b4a6 --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/key_value_generated.rs @@ -0,0 +1,144 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +pub enum KeyValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct KeyValue<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for KeyValue<'a> { + type Inner = KeyValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> KeyValue<'a> { + pub const VT_KEY: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + KeyValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args KeyValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = KeyValueBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + if let Some(x) = args.key { + builder.add_key(x); + } + builder.finish() + } + + #[inline] + pub fn key(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(KeyValue::VT_KEY, None) + .unwrap() + } + } + #[inline] + pub fn value(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(KeyValue::VT_VALUE, None) + .unwrap() + } + } +} + +impl flatbuffers::Verifiable for KeyValue<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("key", Self::VT_KEY, true)? + .visit_field::>("value", Self::VT_VALUE, true)? + .finish(); + Ok(()) + } +} +pub struct KeyValueArgs<'a> { + pub key: Option>, + pub value: Option>, +} +impl<'a> Default for KeyValueArgs<'a> { + #[inline] + fn default() -> Self { + KeyValueArgs { + key: None, // required field + value: None, // required field + } + } +} + +pub struct KeyValueBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> KeyValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_key(&mut self, key: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(KeyValue::VT_KEY, key); + } + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(KeyValue::VT_VALUE, value); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> KeyValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + KeyValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, KeyValue::VT_KEY, "key"); + self.fbb_.required(o, KeyValue::VT_VALUE, "value"); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for KeyValue<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("KeyValue"); + ds.field("key", &self.key()); + ds.field("value", &self.value()); + ds.finish() + } +} diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/log_event_type_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/log_event_type_generated.rs new file mode 100644 index 000000000..c78c37a59 --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/log_event_type_generated.rs @@ -0,0 +1,195 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +pub enum LogEventTypeOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct LogEventType<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for LogEventType<'a> { + type Inner = LogEventType<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> LogEventType<'a> { + pub const VT_PARENT_ID: flatbuffers::VOffsetT = 4; + pub const VT_NAME: flatbuffers::VOffsetT = 6; + pub const VT_TSC: flatbuffers::VOffsetT = 8; + pub const VT_FIELDS: flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + LogEventType { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args LogEventTypeArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = LogEventTypeBuilder::new(_fbb); + builder.add_tsc(args.tsc); + builder.add_parent_id(args.parent_id); + if let Some(x) = args.fields { + builder.add_fields(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn parent_id(&self) -> u64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(LogEventType::VT_PARENT_ID, Some(0)) + .unwrap() + } + } + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(LogEventType::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn tsc(&self) -> u64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(LogEventType::VT_TSC, Some(0)).unwrap() } + } + #[inline] + pub fn fields( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(LogEventType::VT_FIELDS, None) + } + } +} + +impl flatbuffers::Verifiable for LogEventType<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("parent_id", Self::VT_PARENT_ID, false)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::("tsc", Self::VT_TSC, false)? + .visit_field::>, + >>("fields", Self::VT_FIELDS, false)? + .finish(); + Ok(()) + } +} +pub struct LogEventTypeArgs<'a> { + pub parent_id: u64, + pub name: Option>, + pub tsc: u64, + pub fields: Option< + flatbuffers::WIPOffset>>>, + >, +} +impl<'a> Default for LogEventTypeArgs<'a> { + #[inline] + fn default() -> Self { + LogEventTypeArgs { + parent_id: 0, + name: None, // required field + tsc: 0, + fields: None, + } + } +} + +pub struct LogEventTypeBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> LogEventTypeBuilder<'a, 'b, A> { + #[inline] + pub fn add_parent_id(&mut self, parent_id: u64) { + self.fbb_ + .push_slot::(LogEventType::VT_PARENT_ID, parent_id, 0); + } + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(LogEventType::VT_NAME, name); + } + #[inline] + pub fn add_tsc(&mut self, tsc: u64) { + self.fbb_.push_slot::(LogEventType::VT_TSC, tsc, 0); + } + #[inline] + pub fn add_fields( + &mut self, + fields: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(LogEventType::VT_FIELDS, fields); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> LogEventTypeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + LogEventTypeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, LogEventType::VT_NAME, "name"); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for LogEventType<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("LogEventType"); + ds.field("parent_id", &self.parent_id()); + ds.field("name", &self.name()); + ds.field("tsc", &self.tsc()); + ds.field("fields", &self.fields()); + ds.finish() + } +} diff --git a/src/hyperlight_common/src/flatbuffers/hyperlight/generated/open_span_type_generated.rs b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/open_span_type_generated.rs new file mode 100644 index 000000000..d5d9ac8ef --- /dev/null +++ b/src/hyperlight_common/src/flatbuffers/hyperlight/generated/open_span_type_generated.rs @@ -0,0 +1,235 @@ +// automatically generated by the FlatBuffers compiler, do not modify +// @generated +extern crate alloc; +extern crate flatbuffers; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::mem; + +use self::flatbuffers::{EndianScalar, Follow}; +use super::*; +pub enum OpenSpanTypeOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct OpenSpanType<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for OpenSpanType<'a> { + type Inner = OpenSpanType<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> OpenSpanType<'a> { + pub const VT_ID: flatbuffers::VOffsetT = 4; + pub const VT_PARENT: flatbuffers::VOffsetT = 6; + pub const VT_NAME: flatbuffers::VOffsetT = 8; + pub const VT_TARGET: flatbuffers::VOffsetT = 10; + pub const VT_TSC: flatbuffers::VOffsetT = 12; + pub const VT_FIELDS: flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + OpenSpanType { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args OpenSpanTypeArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = OpenSpanTypeBuilder::new(_fbb); + builder.add_tsc(args.tsc); + if let Some(x) = args.parent { + builder.add_parent(x); + } + builder.add_id(args.id); + if let Some(x) = args.fields { + builder.add_fields(x); + } + if let Some(x) = args.target { + builder.add_target(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn id(&self) -> u64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(OpenSpanType::VT_ID, Some(0)).unwrap() } + } + #[inline] + pub fn parent(&self) -> Option { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(OpenSpanType::VT_PARENT, None) } + } + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(OpenSpanType::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn target(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(OpenSpanType::VT_TARGET, None) + .unwrap() + } + } + #[inline] + pub fn tsc(&self) -> u64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(OpenSpanType::VT_TSC, Some(0)).unwrap() } + } + #[inline] + pub fn fields( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(OpenSpanType::VT_FIELDS, None) + } + } +} + +impl flatbuffers::Verifiable for OpenSpanType<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("id", Self::VT_ID, false)? + .visit_field::("parent", Self::VT_PARENT, false)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>("target", Self::VT_TARGET, true)? + .visit_field::("tsc", Self::VT_TSC, false)? + .visit_field::>, + >>("fields", Self::VT_FIELDS, false)? + .finish(); + Ok(()) + } +} +pub struct OpenSpanTypeArgs<'a> { + pub id: u64, + pub parent: Option, + pub name: Option>, + pub target: Option>, + pub tsc: u64, + pub fields: Option< + flatbuffers::WIPOffset>>>, + >, +} +impl<'a> Default for OpenSpanTypeArgs<'a> { + #[inline] + fn default() -> Self { + OpenSpanTypeArgs { + id: 0, + parent: None, + name: None, // required field + target: None, // required field + tsc: 0, + fields: None, + } + } +} + +pub struct OpenSpanTypeBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> OpenSpanTypeBuilder<'a, 'b, A> { + #[inline] + pub fn add_id(&mut self, id: u64) { + self.fbb_.push_slot::(OpenSpanType::VT_ID, id, 0); + } + #[inline] + pub fn add_parent(&mut self, parent: u64) { + self.fbb_ + .push_slot_always::(OpenSpanType::VT_PARENT, parent); + } + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(OpenSpanType::VT_NAME, name); + } + #[inline] + pub fn add_target(&mut self, target: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(OpenSpanType::VT_TARGET, target); + } + #[inline] + pub fn add_tsc(&mut self, tsc: u64) { + self.fbb_.push_slot::(OpenSpanType::VT_TSC, tsc, 0); + } + #[inline] + pub fn add_fields( + &mut self, + fields: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(OpenSpanType::VT_FIELDS, fields); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> OpenSpanTypeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + OpenSpanTypeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, OpenSpanType::VT_NAME, "name"); + self.fbb_.required(o, OpenSpanType::VT_TARGET, "target"); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for OpenSpanType<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("OpenSpanType"); + ds.field("id", &self.id()); + ds.field("parent", &self.parent()); + ds.field("name", &self.name()); + ds.field("target", &self.target()); + ds.field("tsc", &self.tsc()); + ds.field("fields", &self.fields()); + ds.finish() + } +} diff --git a/src/hyperlight_common/src/flatbuffers/mod.rs b/src/hyperlight_common/src/flatbuffers/mod.rs index bf8582877..1bf99d77f 100644 --- a/src/hyperlight_common/src/flatbuffers/mod.rs +++ b/src/hyperlight_common/src/flatbuffers/mod.rs @@ -20,6 +20,8 @@ pub mod hyperlight { pub use self::function_call_type_generated::*; mod log_level_generated; pub use self::log_level_generated::*; + mod guest_event_type_generated; + pub use self::guest_event_type_generated::*; mod hlint_generated; pub use self::hlint_generated::*; mod hluint_generated; @@ -54,6 +56,18 @@ pub mod hyperlight { pub use self::function_call_generated::*; mod guest_log_data_generated; pub use self::guest_log_data_generated::*; + mod key_value_generated; + pub use self::key_value_generated::*; + mod open_span_type_generated; + pub use self::open_span_type_generated::*; + mod close_span_type_generated; + pub use self::close_span_type_generated::*; + mod log_event_type_generated; + pub use self::log_event_type_generated::*; + mod guest_event_envelope_type_generated; + pub use self::guest_event_envelope_type_generated::*; + mod guest_trace_data_type_generated; + pub use self::guest_trace_data_type_generated::*; mod host_function_definition_generated; pub use self::host_function_definition_generated::*; mod host_function_details_generated; diff --git a/src/hyperlight_guest/src/exit.rs b/src/hyperlight_guest/src/exit.rs index cc91aad9e..449d1ddc2 100644 --- a/src/hyperlight_guest/src/exit.rs +++ b/src/hyperlight_guest/src/exit.rs @@ -34,9 +34,8 @@ pub fn halt() { unsafe { asm!("hlt", in("r8") OutBAction::TraceBatch as u64, - in("r9") tbi.guest_start_tsc, - in("r10") tbi.spans_ptr, - in("r11") tbi.events_ptr, + in("r9") tbi.serialized_data.as_ptr() as u64, + in("r10") tbi.serialized_data.len() as u64, options(nostack) ) }; @@ -134,9 +133,8 @@ pub(crate) unsafe fn out32(port: u16, val: u32) { in("dx") port, in("eax") val, in("r8") OutBAction::TraceBatch as u64, - in("r9") tbi.guest_start_tsc, - in("r10") tbi.spans_ptr, - in("r11") tbi.events_ptr, + in("r9") tbi.serialized_data.as_ptr() as u64, + in("r10") tbi.serialized_data.len() as u64, options(preserves_flags, nomem, nostack) ) }; diff --git a/src/hyperlight_guest_tracing/Cargo.toml b/src/hyperlight_guest_tracing/Cargo.toml index 5109aba85..4b8a92a79 100644 --- a/src/hyperlight_guest_tracing/Cargo.toml +++ b/src/hyperlight_guest_tracing/Cargo.toml @@ -10,7 +10,6 @@ readme.workspace = true description = """Provides the tracing functionality for the hyperlight guest.""" [dependencies] -heapless = { version = "0.9.1", features = ["serde"] } hyperlight-common = { workspace = true, default-features = false } spin = "0.10.0" tracing = { version = "0.1.41", default-features = false, features = ["attributes"] } diff --git a/src/hyperlight_guest_tracing/src/lib.rs b/src/hyperlight_guest_tracing/src/lib.rs index ded4e91aa..eda7aada5 100644 --- a/src/hyperlight_guest_tracing/src/lib.rs +++ b/src/hyperlight_guest_tracing/src/lib.rs @@ -15,7 +15,6 @@ limitations under the License. */ #![no_std] -use heapless as hl; /// Expose invariant TSC module pub mod invariant_tsc; @@ -42,102 +41,6 @@ pub use trace::{ set_start_tsc, }; -/// Maximum number of spans that the guest can store -const MAX_NO_OF_SPANS: usize = 10; -/// Maximum number of events that the guest can store -const MAX_NO_OF_EVENTS: usize = 10; -/// Maximum length a name can have in a span/event -const MAX_NAME_LENGTH: usize = 64; -/// Maximum length the target can have in a span/event -const MAX_TARGET_LENGTH: usize = 64; -/// Maximum length key of a Field can have -const MAX_FIELD_KEY_LENGTH: usize = 32; -/// Maximum length value of a Field can have -const MAX_FIELD_VALUE_LENGTH: usize = 96; -/// Maximum number of fields a span/event can have -const MAX_NO_OF_FIELDS: usize = 8; - -/// Alias for the complicated heapless::Vec type for Spans -pub type Spans = hl::Vec< - GuestSpan< - MAX_NAME_LENGTH, - MAX_TARGET_LENGTH, - MAX_FIELD_KEY_LENGTH, - MAX_FIELD_VALUE_LENGTH, - MAX_NO_OF_FIELDS, - >, - MAX_NO_OF_SPANS, ->; - -/// Alias for the complicated heapless::Vec type for Events -pub type Events = hl::Vec< - GuestEvent, - MAX_NO_OF_EVENTS, ->; - -/// The trace level assigned to a span/event -#[derive(Debug, Copy, Clone)] -pub enum TraceLevel { - Error, - Warn, - Info, - Debug, - Trace, -} - -impl From for TraceLevel { - fn from(value: tracing::Level) -> Self { - match value { - tracing::Level::ERROR => Self::Error, - tracing::Level::WARN => Self::Warn, - tracing::Level::INFO => Self::Info, - tracing::Level::DEBUG => Self::Debug, - tracing::Level::TRACE => Self::Trace, - } - } -} -impl From for tracing::Level { - fn from(value: TraceLevel) -> Self { - match value { - TraceLevel::Error => Self::ERROR, - TraceLevel::Warn => Self::WARN, - TraceLevel::Info => Self::INFO, - TraceLevel::Debug => Self::DEBUG, - TraceLevel::Trace => Self::TRACE, - } - } -} - -/// The structure in which a guest stores Span information -pub struct GuestSpan< - const N: usize, - const T: usize, - const FK: usize, - const FV: usize, - const F: usize, -> { - pub id: u64, - pub parent_id: Option, - pub level: TraceLevel, - /// Span name - pub name: hl::String, - /// Filename - pub target: hl::String, - pub start_tsc: u64, - pub end_tsc: Option, - pub fields: hl::Vec<(hl::String, hl::String), F>, -} - -/// The structure in which a guest stores Event information -pub struct GuestEvent { - pub parent_id: u64, - pub level: TraceLevel, - pub name: hl::String, - /// Event name - pub tsc: u64, - pub fields: hl::Vec<(hl::String, hl::String), F>, -} - /// This module is gated because some of these types are also used on the host, but we want /// only the guest to allocate and allow the functionality intended for the guest. #[cfg(feature = "trace")] diff --git a/src/hyperlight_guest_tracing/src/state.rs b/src/hyperlight_guest_tracing/src/state.rs index f7196a350..80dff18cf 100644 --- a/src/hyperlight_guest_tracing/src/state.rs +++ b/src/hyperlight_guest_tracing/src/state.rs @@ -15,84 +15,62 @@ limitations under the License. */ extern crate alloc; +use alloc::string::String; +use alloc::vec::Vec; use core::sync::atomic::{AtomicU64, Ordering}; -use heapless as hl; +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; -use crate::{ - GuestEvent, GuestSpan, MAX_FIELD_KEY_LENGTH, MAX_FIELD_VALUE_LENGTH, MAX_NAME_LENGTH, - MAX_NO_OF_EVENTS, MAX_NO_OF_FIELDS, MAX_NO_OF_SPANS, MAX_TARGET_LENGTH, invariant_tsc, -}; pub struct TraceBatchInfo { - /// The timestamp counter at the start of the guest execution. - pub guest_start_tsc: u64, - /// Pointer to the spans in the guest memory. - pub spans_ptr: u64, - /// Pointer to the events in the guest memory. - pub events_ptr: u64, + pub serialized_data: Vec, } -/// Helper type to define the guest state with the configured constants -pub type GuestState = TraceState< - MAX_NO_OF_SPANS, - MAX_NO_OF_EVENTS, - MAX_NAME_LENGTH, - MAX_TARGET_LENGTH, - MAX_FIELD_KEY_LENGTH, - MAX_FIELD_VALUE_LENGTH, - MAX_NO_OF_FIELDS, ->; - /// Internal state of the tracing subscriber -pub(crate) struct TraceState< - const SP: usize, - const EV: usize, - const N: usize, - const T: usize, - const FK: usize, - const FV: usize, - const F: usize, -> { +pub(crate) struct GuestState { /// Whether we need to cleanup the state on next access cleanup_needed: bool, - /// The timestamp counter at the start of the guest execution. - guest_start_tsc: u64, /// Next span ID to allocate next_id: AtomicU64, - /// All spans collected - spans: hl::Vec, SP>, - /// All events collected - events: hl::Vec, EV>, + /// Trace information that is exchanged with the host + data: GuestTraceData, /// Stack of active spans - stack: hl::Vec, + stack: Vec, } -impl< - const SP: usize, - const EV: usize, - const N: usize, - const T: usize, - const FK: usize, - const FV: usize, - const F: usize, -> TraceState -{ +/// TODO: Change these constants to be configurable at runtime by the guest +/// Maybe use a weak symbol that the guest can override at link time? +/// +/// Pre-calculated capacity for the events vector +/// This is to avoid reallocations in the guest +/// We allocate space for both spans and events +const EVENTS_VEC_CAPACITY: usize = 30; +/// Maximum number of spans that can be active at the same time +/// This is half of the events capacity because there are two events per span +/// (open and close) +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, - guest_start_tsc, next_id: AtomicU64::new(1), - spans: hl::Vec::new(), - stack: hl::Vec::new(), - events: hl::Vec::new(), + data: GuestTraceData { + start_tsc: guest_start_tsc, + events: Vec::with_capacity(EVENTS_VEC_CAPACITY), + }, + stack: Vec::with_capacity(MAX_NO_OF_SPANS), } } + /// Allocate a new ID for a span + /// Returns the numeric ID and the tracing ID + /// This shall return unique IDs for each call pub(crate) fn alloc_id(&self) -> (u64, Id) { let n = self.next_id.load(Ordering::Relaxed); self.next_id.store(n + 1, Ordering::Relaxed); @@ -104,11 +82,8 @@ impl< /// This ensures that after a VM exit, we keep the spans that /// are still active (in the stack) and remove all other spans and events. pub fn clean(&mut self) { - // Remove all spans that have an end timestamp (closed spans) - self.spans.retain(|s| s.end_tsc.is_none()); - // Remove all events - self.events.clear(); + self.data.events.clear(); } #[inline(always)] @@ -122,9 +97,7 @@ impl< /// Triggers a VM exit to flush the current spans to the host. /// This also clears the internal state to start fresh. fn send_to_host(&mut self) { - let guest_start_tsc = self.guest_start_tsc; - let spans_ptr = &self.spans as *const _ as u64; - let events_ptr = &self.events as *const _ as u64; + let tb = self.guest_trace_info(); unsafe { core::arch::asm!("out dx, al", @@ -132,9 +105,8 @@ impl< in("dx") OutBAction::TraceBatch as u16, // Additional magic number to identify the action in("r8") OutBAction::TraceBatch as u64, - in("r9") guest_start_tsc, - in("r10") spans_ptr, - in("r11") events_ptr, + in("r9") tb.serialized_data.as_ptr() as u64, + in("r10") tb.serialized_data.len() as u64, ); } @@ -143,21 +115,26 @@ impl< /// Set a new guest start tsc pub(crate) fn set_start_tsc(&mut self, guest_start_tsc: u64) { - self.guest_start_tsc = guest_start_tsc; + self.data.start_tsc = guest_start_tsc; } /// Closes the trace by ending all spans /// NOTE: This expects an outb call to send the spans to the host. pub(crate) fn end_trace(&mut self) { - for span in self.spans.iter_mut() { - if span.end_tsc.is_none() { - span.end_tsc = Some(invariant_tsc::read_tsc()); - } - } - // Empty the stack - while self.stack.pop().is_some() { + while let Some(id) = self.stack.pop() { // Pop all remaining spans from the stack + let event = GuestEvent::CloseSpan { + id, + tsc: invariant_tsc::read_tsc(), + }; + let events = &mut self.data.events; + // Should never fail because we flush when full + events.push(event); + + if events.len() >= EVENTS_VEC_CAPACITY { + self.send_to_host(); + } } // Mark for clearing when re-entering the VM because we might @@ -167,11 +144,8 @@ impl< /// Returns information about the information needed by the host to read the spans. pub(crate) fn guest_trace_info(&mut self) -> TraceBatchInfo { - TraceBatchInfo { - guest_start_tsc: self.guest_start_tsc, - spans_ptr: &self.spans as *const _ as u64, - events_ptr: &self.events as *const _ as u64, - } + let serialized_data: Vec = Vec::from(&self.data); + TraceBatchInfo { serialized_data } } /// Create a new span and push it on the stack @@ -180,36 +154,31 @@ impl< let (idn, id) = self.alloc_id(); let md = attrs.metadata(); - let mut name = hl::String::::new(); - let mut target = hl::String::::new(); - // Shorten name and target if they are bigger than the space allocated - let _ = name.push_str(&md.name()[..usize::min(md.name().len(), name.capacity())]); - let _ = target.push_str(&md.target()[..usize::min(md.target().len(), target.capacity())]); + let name = String::from(md.name()); + let target = String::from(md.target()); // Visit fields to collect them - let mut fields = hl::Vec::new(); - attrs.record(&mut FieldsVisitor:: { out: &mut fields }); + let mut fields = Vec::new(); + attrs.record(&mut FieldsVisitor { out: &mut fields }); // Find parent from current stack top (if any) let parent_id = self.stack.last().copied(); - let span = GuestSpan:: { + let event = GuestEvent::OpenSpan { id: idn, parent_id, - level: (*md.level()).into(), name, target, - start_tsc: invariant_tsc::read_tsc(), - end_tsc: None, + tsc: invariant_tsc::read_tsc(), fields, }; - let spans = &mut self.spans; + let events = &mut self.data.events; // Should never fail because we flush when full - let _ = spans.push(span); + events.push(event); // In case the spans Vec is full, we need to report them to the host - if spans.len() == spans.capacity() { + if events.len() >= EVENTS_VEC_CAPACITY { self.send_to_host(); } @@ -223,44 +192,47 @@ impl< let parent_id = stack.last().copied().unwrap_or(0); let md = event.metadata(); - let mut name = hl::String::::new(); - // Shorten name and target if they are bigger than the space allocated - let _ = name.push_str(&md.name()[..usize::min(md.name().len(), name.capacity())]); + let name = String::from(md.name()); - let mut fields = hl::Vec::new(); - event.record(&mut FieldsVisitor:: { out: &mut fields }); + let mut fields = Vec::new(); + event.record(&mut FieldsVisitor { out: &mut fields }); - let ev = GuestEvent { + let ev = GuestEvent::LogEvent { parent_id, - level: (*md.level()).into(), name, tsc: invariant_tsc::read_tsc(), fields, }; // Should never fail because we flush when full - let _ = self.events.push(ev); + self.data.events.push(ev); // Flush buffer to host if full - if self.events.len() >= self.events.capacity() { + if self.data.events.len() >= EVENTS_VEC_CAPACITY { self.send_to_host(); } } /// Record new values for an existing span - pub(crate) fn record(&mut self, id: &Id, values: &Record<'_>) { - let spans = &mut self.spans; - if let Some(s) = spans.iter_mut().find(|s| s.id == id.into_u64()) { - let mut v = hl::Vec::new(); - values.record(&mut FieldsVisitor:: { out: &mut v }); - s.fields.extend(v); + 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); } } /// Enter a span (push it on the stack) pub(crate) fn enter(&mut self, id: &Id) { let st = &mut self.stack; - let _ = st.push(id.into_u64()); + st.push(id.into_u64()); } /// Exit a span (pop it from the stack) @@ -272,12 +244,22 @@ impl< /// Try to close a span by ID, returning true if successful /// Records the end timestamp for the span. pub(crate) fn try_close(&mut self, id: Id) -> bool { - let spans = &mut self.spans; - if let Some(s) = spans.iter_mut().find(|s| s.id == id.into_u64()) { - s.end_tsc = Some(invariant_tsc::read_tsc()); - true - } else { - false + let events = &mut self.data.events; + + let event = GuestEvent::CloseSpan { + id: id.into_u64(), + tsc: invariant_tsc::read_tsc(), + }; + + // Should never fail because we flush when full + events.push(event); + + if events.len() >= EVENTS_VEC_CAPACITY { + self.send_to_host(); } + + // We do not keep the span data in for the duration of the open span + // but rather just log the close event. + true } } diff --git a/src/hyperlight_guest_tracing/src/visitor.rs b/src/hyperlight_guest_tracing/src/visitor.rs index 7955ca77e..df0baea3b 100644 --- a/src/hyperlight_guest_tracing/src/visitor.rs +++ b/src/hyperlight_guest_tracing/src/visitor.rs @@ -15,59 +15,49 @@ limitations under the License. */ extern crate alloc; +use alloc::string::String; +use alloc::vec::Vec; use core::fmt::Debug; -use heapless as hl; +use hyperlight_common::flatbuffer_wrappers::guest_trace_data::KeyValue; use tracing_core::field::{Field, Visit}; /// Visitor implementation to collect fields into a vector of key-value pairs -pub(crate) struct FieldsVisitor<'a, const FK: usize, const FV: usize, const F: usize> { - pub out: &'a mut hl::Vec<(hl::String, hl::String), F>, +pub(crate) struct FieldsVisitor<'a> { + pub out: &'a mut Vec, } -impl Visit for FieldsVisitor<'_, FK, FV, F> { +impl<'a> Visit for FieldsVisitor<'a> { /// Record a byte slice field /// # Arguments /// * `field` - The field metadata /// * `value` - The byte slice value - /// NOTE: This implementation truncates the key and value if they exceed the allocated capacity - fn record_bytes(&mut self, field: &Field, value: &[u8]) { - let mut k = hl::String::::new(); - let mut val = hl::String::::new(); - // Shorten key and value if they are bigger than the space allocated - let _ = k.push_str(&field.name()[..usize::min(field.name().len(), k.capacity())]); - let _ = - val.push_str(&alloc::format!("{value:?}")[..usize::min(value.len(), val.capacity())]); - let _ = self.out.push((k, val)); + fn record_bytes(&mut self, f: &Field, v: &[u8]) { + let k = String::from(f.name()); + let mut val = String::new(); + val.push_str(&alloc::format!("{v:?}")); + self.out.push(KeyValue { key: k, value: val }); } /// Record a string field /// # Arguments /// * `f` - The field metadata /// * `v` - The string value - /// NOTE: This implementation truncates the key and value if they exceed the allocated capacity fn record_str(&mut self, f: &Field, v: &str) { - let mut k = heapless::String::::new(); - let mut val = heapless::String::::new(); - // Shorten key and value if they are bigger than the space allocated - let _ = k.push_str(&f.name()[..usize::min(f.name().len(), k.capacity())]); - let _ = val.push_str(&v[..usize::min(v.len(), val.capacity())]); - let _ = self.out.push((k, val)); + let k = String::from(f.name()); + let mut val = String::new(); + val.push_str(v); + self.out.push(KeyValue { key: k, value: val }); } /// Record a debug field /// # Arguments /// * `f` - The field metadata /// * `v` - The debug value - /// NOTE: This implementation truncates the key and value if they exceed the allocated capacity fn record_debug(&mut self, f: &Field, v: &dyn Debug) { - use heapless::String; - let mut k = String::::new(); - let mut val = String::::new(); - // Shorten key and value if they are bigger than the space allocated - let _ = k.push_str(&f.name()[..usize::min(f.name().len(), k.capacity())]); - let v = alloc::format!("{v:?}"); - let _ = val.push_str(&v[..usize::min(v.len(), val.capacity())]); - let _ = self.out.push((k, val)); + let k = String::from(f.name()); + let mut val = String::new(); + val.push_str(&alloc::format!("{v:?}")); + self.out.push(KeyValue { key: k, value: val }); } } diff --git a/src/hyperlight_host/src/hypervisor/hyperv_linux.rs b/src/hyperlight_host/src/hypervisor/hyperv_linux.rs index 512cf3a2c..64074b5f9 100644 --- a/src/hyperlight_host/src/hypervisor/hyperv_linux.rs +++ b/src/hyperlight_host/src/hypervisor/hyperv_linux.rs @@ -1087,7 +1087,7 @@ impl Hypervisor for HypervLinuxDriver { let regs = self.regs()?; tc.handle_trace( ®s, - self.mem_mgr.as_ref().ok_or_else(|| { + self.mem_mgr.as_mut().ok_or_else(|| { new_error!("Memory manager is not initialized before handling trace") })?, ) diff --git a/src/hyperlight_host/src/hypervisor/hyperv_windows.rs b/src/hyperlight_host/src/hypervisor/hyperv_windows.rs index 6e880c943..ca1f75997 100644 --- a/src/hyperlight_host/src/hypervisor/hyperv_windows.rs +++ b/src/hyperlight_host/src/hypervisor/hyperv_windows.rs @@ -976,7 +976,7 @@ impl Hypervisor for HypervWindowsDriver { let regs = self.regs()?; tc.handle_trace( ®s, - self.mem_mgr.as_ref().ok_or_else(|| { + self.mem_mgr.as_mut().ok_or_else(|| { new_error!("Memory manager is not initialized before handling trace") })?, ) diff --git a/src/hyperlight_host/src/hypervisor/kvm.rs b/src/hyperlight_host/src/hypervisor/kvm.rs index 03f3cd7a3..04b8ed60f 100644 --- a/src/hyperlight_host/src/hypervisor/kvm.rs +++ b/src/hyperlight_host/src/hypervisor/kvm.rs @@ -1043,7 +1043,7 @@ impl Hypervisor for KVMDriver { let regs = self.regs()?; tc.handle_trace( ®s, - self.mem_mgr.as_ref().ok_or_else(|| { + self.mem_mgr.as_mut().ok_or_else(|| { new_error!("Memory manager is not initialized before handling trace") })?, ) diff --git a/src/hyperlight_host/src/sandbox/trace/context.rs b/src/hyperlight_host/src/sandbox/trace/context.rs index f17a466ef..16d6a5ac3 100644 --- a/src/hyperlight_host/src/sandbox/trace/context.rs +++ b/src/hyperlight_host/src/sandbox/trace/context.rs @@ -17,8 +17,10 @@ limitations under the License. use std::collections::HashMap; use std::time::{Duration, Instant, SystemTime}; +use hyperlight_common::flatbuffer_wrappers::guest_trace_data::{ + GuestEvent, GuestTraceData, KeyValue as GuestKeyValue, +}; use hyperlight_common::outb::OutBAction; -use hyperlight_guest_tracing::{Events, Spans}; use opentelemetry::global::BoxedSpan; use opentelemetry::trace::{Span as _, TraceContextExt, Tracer as _}; use opentelemetry::{Context, KeyValue, global}; @@ -28,69 +30,80 @@ use tracing_opentelemetry::OpenTelemetrySpanExt; use crate::hypervisor::regs::CommonRegisters; use crate::mem::layout::SandboxMemoryLayout; use crate::mem::mgr::SandboxMemoryManager; -use crate::mem::shared_mem::HostSharedMemory; +use crate::mem::shared_mem::{HostSharedMemory, SharedMemory}; use crate::{HyperlightError, Result, new_error}; /// Type that helps get the data from the guest provided the registers and memory access struct TraceBatch { - pub guest_start_tsc: u64, - pub spans: Spans, - pub events: Events, + data: GuestTraceData, } -impl TryFrom<(&CommonRegisters, &SandboxMemoryManager)> for TraceBatch { +impl + TryFrom<( + &CommonRegisters, + &mut SandboxMemoryManager, + )> for TraceBatch +{ type Error = HyperlightError; fn try_from( - (regs, mem_mgr): (&CommonRegisters, &SandboxMemoryManager), + (regs, mem_mgr): ( + &CommonRegisters, + &mut SandboxMemoryManager, + ), ) -> Result { let magic_no = regs.r8; - let guest_start_tsc = regs.r9; - let spans_ptr = regs.r10 as usize; - let events_ptr = regs.r11 as usize; + let trace_data_ptr = regs.r9 as usize; + let trace_data_len = regs.r10 as usize; if magic_no != OutBAction::TraceBatch as u64 { return Err(new_error!("A TraceBatch is not present")); } - // Transmute spans_ptr to Spans type - let mut spans = vec![0u8; std::mem::size_of::()]; - mem_mgr - .shared_mem - .copy_to_slice(&mut spans, spans_ptr - SandboxMemoryLayout::BASE_ADDRESS) - .map_err(|e| { - new_error!( - "Failed to copy guest trace batch from guest memory to host: {:?}", - e + // Extract the GuestTraceData from guest memory + // This involves: + // 1. Using a mutable reference to the memory manager to get exclusive access to the shared memory. + // This is necessary to ensure that no other part of the code is accessing the memory + // while we are reading from it. + // 2. Getting immutable access to the slice of memory that contains the GuestTraceData + // 3. Parsing the slice into a GuestTraceData structure + // + // Error handling is done at each step to ensure that any issues are properly reported. + // This includes logging errors for easier debugging. + // + // The reason for using `with_exclusivity` is to ensure that we have exclusive access + // and avoid allocating new memory, which needs to be correctly aligned for the + // flatbuffer parsing. + let trace_data = mem_mgr.shared_mem.with_exclusivity(|mem| { + let buf_slice = mem + .as_slice() + // Adjust the pointer to be relative to the base address of the sandbox memory + .get( + trace_data_ptr - SandboxMemoryLayout::BASE_ADDRESS + ..trace_data_ptr - SandboxMemoryLayout::BASE_ADDRESS + trace_data_len, ) - })?; - - let spans: Spans = unsafe { - let raw = spans.as_slice() as *const _ as *const Spans; - raw.read_unaligned() - }; - - // Transmute events_ptr to Events type - let mut events = vec![0u8; std::mem::size_of::()]; - mem_mgr - .shared_mem - .copy_to_slice(&mut events, events_ptr - SandboxMemoryLayout::BASE_ADDRESS) - .map_err(|e| { + // Convert the slice to a Result to handle the case where the slice is out of + // bounds and return a proper error message and log the error. + .ok_or_else(|| { + tracing::error!("Failed to get guest trace batch slice from guest memory"); + new_error!("Failed to get guest trace batch slice from guest memory") + })?; + + // Parse the slice into a GuestTraceData structure + let trace_data: GuestTraceData = buf_slice.try_into().map_err(|e| { + tracing::error!( + "Failed to parse guest trace data from guest memory: {:?}", + e + ); new_error!( - "Failed to copy guest trace batch from guest memory to host: {:?}", + "Failed to parse guest trace data from guest memory: {:?}", e ) })?; - let events: Events = unsafe { - let raw = events.as_slice() as *const _ as *const Events; - raw.read_unaligned() - }; + Ok::(trace_data) + })??; - Ok(TraceBatch { - guest_start_tsc, - spans, - events, - }) + Ok(TraceBatch { data: trace_data }) } } @@ -212,7 +225,7 @@ impl TraceContext { pub fn handle_trace( &mut self, regs: &CommonRegisters, - mem_mgr: &SandboxMemoryManager, + mem_mgr: &mut SandboxMemoryManager, ) -> Result<()> { if self.tsc_freq.is_none() { self.calculate_tsc_freq()?; @@ -220,93 +233,118 @@ impl TraceContext { // Get the guest sent info let trace_batch = TraceBatch::try_from((regs, mem_mgr))?; + let trace_batch = trace_batch.data; let tracer = global::tracer("guest-tracer"); - let mut spans_to_remove = vec![]; - - let mut current_active_span = None; - - // Update the spans map - for s in trace_batch.spans.iter() { - let start_ts = self - .calculate_guest_time_relative_to_host(trace_batch.guest_start_tsc, s.start_tsc)?; - let end_ts = s.end_tsc.map(|tsc| { - self.calculate_guest_time_relative_to_host(trace_batch.guest_start_tsc, tsc) - }); - let parent_id = s.parent_id; - let parent_ctx = if let Some(parent_id) = parent_id { - if let Some(span) = self.guest_spans.get(&parent_id) { - Context::new().with_remote_span_context(span.span_context().clone()) - } else if let Some(parent_ctx) = self.current_parent_ctx.as_ref() { - parent_ctx.clone() - } else { - Span::current().context().clone() + + // Stack to keep track of open spans + let mut spans_stack = vec![]; + + // Process each event + for ev in trace_batch.events.into_iter() { + match ev { + GuestEvent::OpenSpan { + id, + parent_id, + name, + target, + tsc, + fields, + } => { + // Calculate start timestamp + let start_ts = + self.calculate_guest_time_relative_to_host(trace_batch.start_tsc, tsc)?; + + // Determine parent context + // Priority: + // 1. If parent_id is set and found in guest_spans, use that + // 2. If current_parent_ctx is set, use that + // 3. Otherwise, use the current span context + let parent_ctx = if let Some(parent_id) = parent_id { + if let Some(span) = self.guest_spans.get(&parent_id) { + Context::new().with_remote_span_context(span.span_context().clone()) + } else if let Some(parent_ctx) = self.current_parent_ctx.as_ref() { + parent_ctx.clone() + } else { + Span::current().context().clone() + } + } else if let Some(parent_ctx) = self.current_parent_ctx.as_ref() { + parent_ctx.clone() + } else { + Span::current().context().clone() + }; + + // Create the span with calculated start time + let mut sb = tracer + .span_builder(name.to_string()) + .with_start_time(start_ts); + // Set target attribute + sb.attributes = Some(vec![KeyValue::new("target", target.to_string())]); + + // Attach to parent context + let mut span = sb.start_with_context(&tracer, &parent_ctx); + + // Set attributes from fields + for GuestKeyValue { key, value } in fields.iter() { + span.set_attribute(KeyValue::new( + key.as_str().to_string(), + value.as_str().to_string(), + )); + } + + // Store the span + self.guest_spans.insert(id, span); + spans_stack.push(id); } - } else if let Some(parent_ctx) = self.current_parent_ctx.as_ref() { - parent_ctx.clone() - } else { - Span::current().context().clone() - }; - - // Get the saved span, modify it and set it back to avoid borrow checker - let mut span = self.guest_spans.remove(&s.id).unwrap_or_else(|| { - let mut sb = tracer - .span_builder(s.name.to_string()) - .with_start_time(start_ts); - sb.attributes = Some(vec![KeyValue::new("target", s.target.to_string())]); - let mut span = sb.start_with_context(&tracer, &parent_ctx); - - for (k, v) in s.fields.iter() { - span.set_attribute(KeyValue::new( - k.as_str().to_string(), - v.as_str().to_string(), - )); + GuestEvent::CloseSpan { id, tsc } => { + // Remove the span and end it + if let Some(mut span) = self.guest_spans.remove(&id) { + let end_ts = + self.calculate_guest_time_relative_to_host(trace_batch.start_tsc, tsc)?; + span.end_with_timestamp(end_ts); + + // The span ids should be closed in order + if let Some(stack_id) = spans_stack.pop() + && stack_id != id + { + tracing::warn!("Guest span with id {} closed out of order", id); + } + } else { + tracing::warn!("Tried to close non-existing guest span with id {}", id); + } + } + GuestEvent::LogEvent { + parent_id, + name, + tsc, + fields, + } => { + let ts = + self.calculate_guest_time_relative_to_host(trace_batch.start_tsc, tsc)?; + + // Add the event to the parent span + // It should always have a parent span + if let Some(span) = self.guest_spans.get_mut(&parent_id) { + let attributes: Vec = fields + .into_iter() + .map(|GuestKeyValue { key, value }| KeyValue::new(key, value)) + .collect(); + span.add_event_with_timestamp(name.to_string(), ts, attributes); + } else { + tracing::warn!( + "Tried to add event to non-existing guest span with id {}", + parent_id + ); + } } - - span - }); - - // If we find an end timestamp it means the span has been closed - // otherwise store it for later - if let Some(ts) = end_ts { - span.end_with_timestamp(ts?); - spans_to_remove.push(s.id); - } else { - current_active_span = - Some(Context::current().with_remote_span_context(span.span_context().clone())); - } - - self.guest_spans.insert(s.id, span); - } - - // Create the events - for ev in trace_batch.events.iter() { - let ts = - self.calculate_guest_time_relative_to_host(trace_batch.guest_start_tsc, ev.tsc)?; - let mut attributes: Vec = ev - .fields - .iter() - .map(|(k, v)| KeyValue::new(k.to_string(), v.to_string())) - .collect(); - - attributes.push(KeyValue::new( - "level", - tracing::Level::from(ev.level).to_string(), - )); - - // Add the event to the parent span - // It should always have a parent span - if let Some(span) = self.guest_spans.get_mut(&ev.parent_id) { - span.add_event_with_timestamp(ev.name.to_string(), ts, attributes); } } - // Remove the spans that have been closed - for id in spans_to_remove.into_iter() { - self.guest_spans.remove(&id); - } - - if let Some(ctx) = current_active_span { + // Set the current active span context as the last span in the stack because we want + // to create a host span that is a child of the last active guest span + if let Some(span) = spans_stack.pop().and_then(|id| self.guest_spans.get(&id)) { + // Set as current active span + let ctx = Context::current().with_remote_span_context(span.span_context().clone()); self.new_host_trace(ctx); }; diff --git a/src/schema/all.fbs b/src/schema/all.fbs index 9e70a50b8..07cebfd8e 100644 --- a/src/schema/all.fbs +++ b/src/schema/all.fbs @@ -3,5 +3,6 @@ include "function_call_result.fbs"; include "function_call.fbs"; include "guest_error.fbs"; include "guest_log_data.fbs"; +include "guest_trace_data.fbs"; include "host_function_definition.fbs"; -include "host_function_details.fbs"; \ No newline at end of file +include "host_function_details.fbs"; diff --git a/src/schema/guest_trace_data.fbs b/src/schema/guest_trace_data.fbs new file mode 100644 index 000000000..09e19037a --- /dev/null +++ b/src/schema/guest_trace_data.fbs @@ -0,0 +1,45 @@ +namespace Hyperlight.Generated; + +table KeyValue { + key: string (required); + value: string (required); +} + +table OpenSpanType { + id: ulong; + parent: ulong = null; + name: string (required); + target: string (required); + tsc: ulong; + fields: [KeyValue]; +} + +table CloseSpanType { + id: ulong; + tsc: ulong; +} + +table LogEventType { + parent_id: ulong; + name: string (required); + tsc: ulong; + fields: [KeyValue]; +} + +// Result-like union +union GuestEventType { + OpenSpanType, + CloseSpanType, + LogEventType, +} + +table GuestEventEnvelopeType { + event: GuestEventType; +} + +table GuestTraceDataType { + start_tsc: ulong; + events: [GuestEventEnvelopeType]; +} + +root_type GuestTraceDataType; diff --git a/src/tests/rust_guests/dummyguest/Cargo.lock b/src/tests/rust_guests/dummyguest/Cargo.lock index febe57d6c..2d8353674 100644 --- a/src/tests/rust_guests/dummyguest/Cargo.lock +++ b/src/tests/rust_guests/dummyguest/Cargo.lock @@ -29,12 +29,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cc" version = "1.2.34" @@ -74,26 +68,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" -dependencies = [ - "hash32", - "serde", - "stable_deref_trait", -] - [[package]] name = "hyperlight-common" version = "0.10.0" @@ -137,7 +111,6 @@ dependencies = [ name = "hyperlight-guest-tracing" version = "0.10.0" dependencies = [ - "heapless", "hyperlight-common", "spin 0.10.0", "tracing", @@ -279,12 +252,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "syn" version = "2.0.107" diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index a9ce6e53d..96689aaae 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -29,12 +29,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cc" version = "1.2.34" @@ -66,26 +60,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" -dependencies = [ - "hash32", - "serde", - "stable_deref_trait", -] - [[package]] name = "hyperlight-common" version = "0.10.0" @@ -129,7 +103,6 @@ dependencies = [ name = "hyperlight-guest-tracing" version = "0.10.0" dependencies = [ - "heapless", "hyperlight-common", "spin 0.10.0", "tracing", @@ -283,12 +256,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "syn" version = "2.0.107" diff --git a/src/tests/rust_guests/witguest/Cargo.lock b/src/tests/rust_guests/witguest/Cargo.lock index 91a34d2ce..d65d56ce3 100644 --- a/src/tests/rust_guests/witguest/Cargo.lock +++ b/src/tests/rust_guests/witguest/Cargo.lock @@ -88,12 +88,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cc" version = "1.2.34" @@ -172,15 +166,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -191,17 +176,6 @@ dependencies = [ "serde", ] -[[package]] -name = "heapless" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" -dependencies = [ - "hash32", - "serde", - "stable_deref_trait", -] - [[package]] name = "hyperlight-common" version = "0.10.0" @@ -272,7 +246,6 @@ dependencies = [ name = "hyperlight-guest-tracing" version = "0.10.0" dependencies = [ - "heapless", "hyperlight-common", "spin 0.10.0", "tracing", @@ -524,12 +497,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "syn" version = "2.0.108"