#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct TraceEvent {
pub ts_ns: u64,
pub entity_id: u32,
pub route_id: u32,
pub span_id: u64,
pub parent_span_id: u64,
pub latency_us: u32,
pub status_code: u16,
pub error_code: u16,
pub event_kind: u16,
pub flags: u16,
}
impl TraceEvent {
#[must_use]
#[allow(clippy::too_many_arguments)]
pub const fn new(
ts_ns: u64,
entity_id: u32,
route_id: u32,
span_id: u64,
parent_span_id: u64,
latency_us: u32,
status_code: u16,
error_code: u16,
event_kind: u16,
flags: u16,
) -> Self {
Self {
ts_ns,
entity_id,
route_id,
span_id,
parent_span_id,
latency_us,
status_code,
error_code,
event_kind,
flags,
}
}
#[must_use]
pub const fn window_index(&self, window_size_ns: u64) -> u32 {
let raw = self.ts_ns / window_size_ns;
if raw > u32::MAX as u64 {
u32::MAX
} else {
raw as u32
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct GpuTraceEventCompact {
pub ts_ns: u64,
pub entity_and_error: u32,
pub latency_us: u32,
}
impl GpuTraceEventCompact {
pub const SIZE: usize = 16;
pub const ERROR_BIT: u32 = 1u32 << 31;
pub const ENTITY_MASK: u32 = 0x7FFF_FFFF;
#[must_use]
pub const fn from_trace_event(ev: &TraceEvent) -> Self {
let entity_bits = ev.entity_id & Self::ENTITY_MASK;
let error_bit = if ev.error_code != 0 {
Self::ERROR_BIT
} else {
0
};
Self {
ts_ns: ev.ts_ns,
entity_and_error: entity_bits | error_bit,
latency_us: ev.latency_us,
}
}
#[must_use]
pub const fn entity_id(&self) -> u32 {
self.entity_and_error & Self::ENTITY_MASK
}
#[must_use]
pub const fn error_nonzero(&self) -> bool {
(self.entity_and_error & Self::ERROR_BIT) != 0
}
}
#[cfg(feature = "std")]
#[must_use]
pub fn pack_compact_event_projection(events: &[TraceEvent]) -> std::vec::Vec<GpuTraceEventCompact> {
events
.iter()
.map(GpuTraceEventCompact::from_trace_event)
.collect()
}
#[cfg(feature = "std")]
#[must_use]
pub fn compact_event_projection_hash(compact: &[GpuTraceEventCompact]) -> [u8; 32] {
let mut buf: std::vec::Vec<u8> =
std::vec::Vec::with_capacity(compact.len() * GpuTraceEventCompact::SIZE);
for ev in compact {
buf.extend_from_slice(&ev.ts_ns.to_le_bytes());
buf.extend_from_slice(&ev.entity_and_error.to_le_bytes());
buf.extend_from_slice(&ev.latency_us.to_le_bytes());
}
crate::hash::sha256(&buf)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn window_index_partitions_at_boundaries() {
let ev = TraceEvent {
ts_ns: 1_500_000_000,
..TraceEvent::default()
};
assert_eq!(ev.window_index(1_000_000_000), 1);
let edge = TraceEvent {
ts_ns: 2_000_000_000,
..TraceEvent::default()
};
assert_eq!(edge.window_index(1_000_000_000), 2);
}
#[test]
fn window_index_floors_at_zero() {
let ev = TraceEvent::default();
assert_eq!(ev.window_index(1_000_000_000), 0);
}
#[test]
fn struct_size_is_stable() {
assert_eq!(core::mem::size_of::<TraceEvent>(), 48);
}
}