diff --git a/Cargo.toml b/Cargo.toml index 0b75f34..a9fac9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ authors = ["Kyle Sabo", "Microsoft"] crate-type = ["rlib"] [features] -unsafe = [] global_filter = [] common_schema = [] default = ["common_schema"] diff --git a/src/layer.rs b/src/layer.rs index 9fa2868..6b8d154 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::time::SystemTime; use std::{pin::Pin, sync::Arc}; use tracelogging::Guid; @@ -14,42 +15,11 @@ use crate::native::{EventMode, EventWriter}; use crate::values::*; use crate::{map_level, native}; -// This version of the struct is packed such that all the fields are references into a single, -// contiguous allocation. The first byte of the allocation is `layout`. -#[cfg(feature = "unsafe")] -#[repr(C)] struct EtwLayerData { - fields: &'static [&'static str], - values: &'static mut [ValueTypes], - indexes: &'static mut [u8], - activity_id: [u8; 16], // // if set, byte 0 is 1 and 64-bit span ID in the lower 8 bytes - related_activity_id: [u8; 16], // if set, byte 0 is 1 and 64-bit span ID in the lower 8 bytes - _p: std::ptr::NonNull, -} - -#[cfg(feature = "unsafe")] -impl Drop for EtwLayerData { - fn drop(&mut self) { - unsafe { std::alloc::dealloc(self._p.as_ptr() as *mut u8, *self._p.as_ptr()) } - } -} - -// Everything in the struct is Send + Sync except the ptr::NonNull. -// We never deref the ptr::NonNull except in drop, for which safety comes from safe usage of the struct itself. -// Therefore we can safely tag the whole thing as Send + Sync. -#[cfg(feature = "unsafe")] -unsafe impl Send for EtwLayerData {} -#[cfg(feature = "unsafe")] -unsafe impl Sync for EtwLayerData {} - -// This version of the struct uses only safe code, but has 3 separate heap allocations. -#[cfg(not(feature = "unsafe"))] -struct EtwLayerData { - fields: Box<[&'static str]>, - values: Box<[ValueTypes]>, - indexes: Box<[u8]>, + fields: Box<[FieldValueIndex]>, activity_id: [u8; 16], // // if set, byte 0 is 1 and 64-bit span ID in the lower 8 bytes related_activity_id: [u8; 16], // if set, byte 0 is 1 and 64-bit span ID in the lower 8 bytes + start_time: SystemTime, } #[doc(hidden)] @@ -421,68 +391,18 @@ where let n = metadata.fields().len(); - // We want to pack the data that needs to be stored on the span as tightly as possible, - // in order to accommodate as many active spans as possible. - // This is off by default though, since unsafe code is unsafe. - #[cfg(feature = "unsafe")] - let mut data = unsafe { - let layout_layout = std::alloc::Layout::new::(); - let fields_layout = std::alloc::Layout::array::<&str>(n).unwrap(); - let values_layout = std::alloc::Layout::array::(n).unwrap(); - let indexes_layout = std::alloc::Layout::array::(n).unwrap(); - - let (layout, fields_offset) = layout_layout.extend(fields_layout).unwrap(); - let (layout, values_offset) = layout.extend(values_layout).unwrap(); - let (layout, indexes_offset) = layout.extend(indexes_layout).unwrap(); - - let block = std::alloc::alloc_zeroed(layout); - if block.is_null() { - panic!(); - } - - let mut layout_field = - std::ptr::NonNull::new(block as *mut std::alloc::Layout).unwrap(); - let fields: &mut [&str] = - std::slice::from_raw_parts_mut(block.add(fields_offset) as *mut &str, n); - let values: &mut [ValueTypes] = - std::slice::from_raw_parts_mut(block.add(values_offset) as *mut ValueTypes, n); - let indexes: &mut [u8] = std::slice::from_raw_parts_mut(block.add(indexes_offset), n); - - *layout_field.as_mut() = layout; - - let mut i = 0; - for field in metadata.fields().iter() { - fields[i] = field.name(); - values[i] = ValueTypes::None; - indexes[i] = i as u8; - i += 1; - } - - indexes.sort_by_key(|idx| fields[*idx as usize]); - - EtwLayerData { - fields, - values, - indexes, - activity_id: [0; 16], - related_activity_id: [0; 16], - _p: std::ptr::NonNull::new(block as *mut std::alloc::Layout).unwrap(), + let mut data = { + let mut v: Vec = Vec::with_capacity(n); + v.resize_with(n, Default::default); + for i in 0..n { + v[i].sort_index = i as u8; } - }; - #[cfg(not(feature = "unsafe"))] - let mut data = { EtwLayerData { - fields: vec![""; n].into_boxed_slice(), - values: vec![ValueTypes::None; n].into_boxed_slice(), - indexes: ([ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - ])[..n] - .to_vec() - .into_boxed_slice(), + fields: v.into_boxed_slice(), activity_id: [0; 16], related_activity_id: [0; 16], + start_time: SystemTime::UNIX_EPOCH, } }; @@ -499,9 +419,7 @@ where }; attrs.values().record(&mut ValueVisitor { - fields: &data.fields, - values: &mut data.values, - indexes: &mut data.indexes, + fields: &mut data.fields, }); // This will unfortunately box data. It would be ideal if we could avoid this second heap allocation, @@ -535,16 +453,17 @@ where &data.activity_id, &data.related_activity_id, &data.fields, - &data.values, map_level(metadata.level()), self.default_keyword, 0, ); + + data.start_time = timestamp; } fn on_exit(&self, id: &span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) { // A span was exited - let timestamp = std::time::SystemTime::now(); + let stop_timestamp = std::time::SystemTime::now(); let span = if let Some(span) = ctx.span(id) { span @@ -564,11 +483,10 @@ where self.provider.as_ref().span_stop( &span, - timestamp, + (data.start_time, stop_timestamp), &data.activity_id, &data.related_activity_id, &data.fields, - &data.values, map_level(metadata.level()), self.default_keyword, 0, @@ -603,9 +521,7 @@ where }; values.record(&mut ValueVisitor { - fields: &data.fields, - values: &mut data.values, - indexes: &mut data.indexes, + fields: &mut data.fields, }); } } diff --git a/src/native/common_schema/etw_cs.rs b/src/native/common_schema/etw_cs.rs index cebd5ce..f990846 100644 --- a/src/native/common_schema/etw_cs.rs +++ b/src/native/common_schema/etw_cs.rs @@ -150,8 +150,7 @@ impl crate::native::EventWriter for CommonSchemaProvider { _timestamp: SystemTime, _activity_id: &[u8; 16], _related_activity_id: &[u8; 16], - _fields: &'b [&'static str], - _values: &'b [ValueTypes], + _fields: &'b [crate::values::FieldValueIndex], _level: u8, _keyword: u64, _event_tag: u32, @@ -163,11 +162,10 @@ impl crate::native::EventWriter for CommonSchemaProvider { fn span_stop<'a, 'b, R>( self: Pin<&Self>, span: &'b SpanRef<'a, R>, - timestamp: SystemTime, + start_stop_times: (std::time::SystemTime, std::time::SystemTime), _activity_id: &[u8; 16], _related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, @@ -197,7 +195,7 @@ impl crate::native::EventWriter for CommonSchemaProvider { eb.add_struct("PartA", 2 /* + exts.len() as u8*/, 0); { let time: String = - chrono::DateTime::to_rfc3339(&chrono::DateTime::::from(timestamp)); + chrono::DateTime::to_rfc3339(&chrono::DateTime::::from(start_stop_times.1)); eb.add_str8("time", time, OutType::Utf8, 0); eb.add_struct("ext_dt", 2, 0); @@ -238,11 +236,10 @@ impl crate::native::EventWriter for CommonSchemaProvider { eb.add_str8("name", span_name, OutType::Utf8, 0); - // TODO eb.add_str8( "startTime", &chrono::DateTime::to_rfc3339(&chrono::DateTime::::from( - timestamp, + start_stop_times.0, )), OutType::Utf8, 0, @@ -255,14 +252,14 @@ impl crate::native::EventWriter for CommonSchemaProvider { { let mut pfv = CommonSchemaPartCBuilder { eb: eb.deref_mut() }; - for (f, v) in fields.iter().zip(values.iter()) { + for f in fields { as AddFieldAndValue< CommonSchemaPartCBuilder<'_>, >>::add_field_value( &mut pfv, &FieldAndValue { - field_name: f, - value: v, + field_name: f.field, + value: &f.value, }, ); } diff --git a/src/native/common_schema/user_events_cs.rs b/src/native/common_schema/user_events_cs.rs index 4f95618..0b8e756 100644 --- a/src/native/common_schema/user_events_cs.rs +++ b/src/native/common_schema/user_events_cs.rs @@ -166,8 +166,7 @@ impl crate::native::EventWriter for CommonSchemaProvider { _timestamp: SystemTime, _activity_id: &[u8; 16], _related_activity_id: &[u8; 16], - _fields: &'b [&'static str], - _values: &'b [ValueTypes], + _fields: &'b [crate::values::FieldValueIndex], _level: u8, _keyword: u64, _event_tag: u32, @@ -179,11 +178,10 @@ impl crate::native::EventWriter for CommonSchemaProvider { fn span_stop<'a, 'b, R>( self: Pin<&Self>, span: &'b SpanRef<'a, R>, - timestamp: SystemTime, + start_stop_times: (std::time::SystemTime, std::time::SystemTime), _activity_id: &[u8; 16], _related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, @@ -219,7 +217,7 @@ impl crate::native::EventWriter for CommonSchemaProvider { eb.add_struct("PartA", 2 /* + exts.len() as u8*/, 0); { let time: String = - chrono::DateTime::to_rfc3339(&chrono::DateTime::::from(timestamp)); + chrono::DateTime::to_rfc3339(&chrono::DateTime::::from(start_stop_times.1)); eb.add_str("time", time, FieldFormat::Default, 0); eb.add_struct("ext_dt", 2, 0); @@ -260,11 +258,10 @@ impl crate::native::EventWriter for CommonSchemaProvider { eb.add_str("name", span_name, FieldFormat::Default, 0); - // TODO eb.add_str( "startTime", &chrono::DateTime::to_rfc3339(&chrono::DateTime::::from( - timestamp, + start_stop_times.0, )), FieldFormat::Default, 0, @@ -277,14 +274,14 @@ impl crate::native::EventWriter for CommonSchemaProvider { { let mut pfv = CommonSchemaPartCBuilder { eb: eb.deref_mut() }; - for (f, v) in fields.iter().zip(values.iter()) { + for f in fields { as AddFieldAndValue< CommonSchemaPartCBuilder<'_>, >>::add_field_value( &mut pfv, &FieldAndValue { - field_name: f, - value: v, + field_name: f.field, + value: &f.value, }, ); } diff --git a/src/native/etw.rs b/src/native/etw.rs index 5e841b3..6752419 100644 --- a/src/native/etw.rs +++ b/src/native/etw.rs @@ -158,8 +158,7 @@ impl super::EventWriter for Provider { timestamp: SystemTime, activity_id: &[u8; 16], related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, @@ -183,10 +182,10 @@ impl super::EventWriter for Provider { let mut pfv = PayloadFieldVisitor { eb: eb.deref_mut() }; - for (f, v) in fields.iter().zip(values.iter()) { + for f in fields { as AddFieldAndValue>>::add_field_value(&mut pfv, &FieldAndValue { - field_name: f, - value: v, + field_name: f.field, + value: &f.value, }); } @@ -211,11 +210,10 @@ impl super::EventWriter for Provider { fn span_stop<'a, 'b, R>( self: Pin<&Self>, span: &'b SpanRef<'a, R>, - timestamp: SystemTime, + start_stop_times: (std::time::SystemTime, std::time::SystemTime), activity_id: &[u8; 16], related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, @@ -232,17 +230,17 @@ impl super::EventWriter for Provider { eb.add_systemtime( "stop time", - &Into::::into(timestamp).st, + &Into::::into(start_stop_times.1).st, OutType::DateTimeUtc, 0, ); let mut pfv = PayloadFieldVisitor { eb: eb.deref_mut() }; - for (f, v) in fields.iter().zip(values.iter()) { + for f in fields { as AddFieldAndValue>>::add_field_value(&mut pfv, &FieldAndValue { - field_name: f, - value: v, + field_name: f.field, + value: &f.value, }); } diff --git a/src/native/mod.rs b/src/native/mod.rs index ce5f10c..40a031c 100644 --- a/src/native/mod.rs +++ b/src/native/mod.rs @@ -80,8 +80,7 @@ pub trait EventWriter { timestamp: std::time::SystemTime, activity_id: &[u8; 16], related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [crate::values::ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, @@ -91,11 +90,10 @@ pub trait EventWriter { fn span_stop<'a, 'b, R>( self: std::pin::Pin<&Self>, span: &'b tracing_subscriber::registry::SpanRef<'a, R>, - timestamp: std::time::SystemTime, + start_stop_times: (std::time::SystemTime, std::time::SystemTime), activity_id: &[u8; 16], related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [crate::values::ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, diff --git a/src/native/noop.rs b/src/native/noop.rs index 5f5bc23..89cef51 100644 --- a/src/native/noop.rs +++ b/src/native/noop.rs @@ -46,8 +46,7 @@ impl crate::native::EventWriter for Provider { _timestamp: SystemTime, _activity_id: &[u8; 16], _related_activity_id: &[u8; 16], - _fields: &'b [&'static str], - _values: &'b [ValueTypes], + _fields: &'b [crate::values::FieldValueIndex], _level: u8, _keyword: u64, _event_tag: u32, @@ -59,11 +58,10 @@ impl crate::native::EventWriter for Provider { fn span_stop<'a, 'b, R>( self: Pin<&Self>, _span: &'b SpanRef<'a, R>, - _timestamp: SystemTime, + _start_stop_times: (std::time::SystemTime, std::time::SystemTime), _activity_id: &[u8; 16], _related_activity_id: &[u8; 16], - _fields: &'b [&'static str], - _values: &'b [ValueTypes], + _fields: &'b [crate::values::FieldValueIndex], _level: u8, _keyword: u64, _event_tag: u32, diff --git a/src/native/user_events.rs b/src/native/user_events.rs index cd8bf7a..34fe005 100644 --- a/src/native/user_events.rs +++ b/src/native/user_events.rs @@ -152,8 +152,7 @@ impl crate::native::EventWriter for Provider { timestamp: SystemTime, activity_id: &[u8; 16], related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, @@ -186,10 +185,10 @@ impl crate::native::EventWriter for Provider { let mut pfv = PayloadFieldVisitor { eb: eb.deref_mut() }; - for (f, v) in fields.iter().zip(values.iter()) { + for f in fields { as AddFieldAndValue>>::add_field_value(&mut pfv, &FieldAndValue { - field_name: f, - value: v, + field_name: f.field, + value: &f.value, }); } @@ -212,11 +211,10 @@ impl crate::native::EventWriter for Provider { fn span_stop<'a, 'b, R>( self: Pin<&Self>, span: &'b SpanRef<'a, R>, - timestamp: SystemTime, + start_stop_times: (std::time::SystemTime, std::time::SystemTime), activity_id: &[u8; 16], related_activity_id: &[u8; 16], - fields: &'b [&'static str], - values: &'b [ValueTypes], + fields: &'b [crate::values::FieldValueIndex], level: u8, keyword: u64, event_tag: u32, @@ -239,7 +237,7 @@ impl crate::native::EventWriter for Provider { eb.add_value( "stop time", - timestamp + start_stop_times.1 .duration_since(std::time::SystemTime::UNIX_EPOCH) .unwrap() .as_secs(), @@ -249,10 +247,10 @@ impl crate::native::EventWriter for Provider { let mut pfv = PayloadFieldVisitor { eb: eb.deref_mut() }; - for (f, v) in fields.iter().zip(values.iter()) { + for f in fields { as AddFieldAndValue>>::add_field_value(&mut pfv, &FieldAndValue { - field_name: f, - value: v, + field_name: f.field, + value: &f.value, }); } diff --git a/src/values.rs b/src/values.rs index 07a0b3b..ffedf70 100644 --- a/src/values.rs +++ b/src/values.rs @@ -80,19 +80,25 @@ pub(crate) struct FieldAndValue<'a> { pub(crate) value: &'a ValueTypes, } +#[doc(hidden)] +#[derive(Default)] +pub struct FieldValueIndex { + pub(crate) field: &'static str, + pub(crate) value: ValueTypes, + pub(crate) sort_index: u8, +} + pub(crate) struct ValueVisitor<'a> { - pub(crate) fields: &'a [&'static str], - pub(crate) values: &'a mut [ValueTypes], - pub(crate) indexes: &'a mut [u8], + pub(crate) fields: &'a mut [FieldValueIndex], } impl<'a> ValueVisitor<'a> { fn update_value(&mut self, field_name: &'static str, value: ValueTypes) { let res = self - .indexes - .binary_search_by_key(&field_name, |idx| self.fields[*idx as usize]); + .fields + .binary_search_by_key(&field_name, |idx| self.fields[idx.sort_index as usize].field); if let Ok(idx) = res { - self.values[self.indexes[idx] as usize] = value; + self.fields[self.fields[idx].sort_index as usize].value = value; } else { // We don't support (and don't need to support) adding new fields that weren't in the original metadata }