diff --git a/libdd-otel-thread-ctx-ffi/src/lib.rs b/libdd-otel-thread-ctx-ffi/src/lib.rs index ec77d043d7..45551ff70a 100644 --- a/libdd-otel-thread-ctx-ffi/src/lib.rs +++ b/libdd-otel-thread-ctx-ffi/src/lib.rs @@ -40,7 +40,7 @@ mod linux { "MAX_ATTRS_DATA_SIZE out of sync with libdd-otel-thread-ctx" ); - /// Allocate and initialise a new thread context. + /// Allocate and initialise a new thread context, including its W3C trace-flags byte. /// /// Returns a non-null owned handle that must eventually be released with /// `ddog_otel_thread_ctx_free`. @@ -48,9 +48,11 @@ mod linux { pub extern "C" fn ddog_otel_thread_ctx_new( trace_id: &[u8; 16], span_id: &[u8; 8], + trace_flags: u8, local_root_span_id: &[u8; 8], ) -> NonNull { - ThreadContext::new(*trace_id, *span_id, *local_root_span_id, &[]).into_opaque_ptr() + ThreadContext::new(*trace_id, *span_id, trace_flags, *local_root_span_id, &[]) + .into_opaque_ptr() } /// Free an owned thread context. @@ -93,7 +95,7 @@ mod linux { ThreadContext::detach().map(ThreadContext::into_opaque_ptr) } - /// Update the currently attached context in-place. + /// Update the currently attached context in-place, including its W3C trace-flags byte. /// /// If no context is currently attached, one is created and attached, equivalent to calling /// `ddog_otel_thread_ctx_new` followed by `ddog_otel_thread_ctx_attach`. @@ -101,8 +103,9 @@ mod linux { pub extern "C" fn ddog_otel_thread_ctx_update( trace_id: &[u8; 16], span_id: &[u8; 8], + trace_flags: u8, local_root_span_id: &[u8; 8], ) { - ThreadContext::update(*trace_id, *span_id, *local_root_span_id, &[]); + ThreadContext::update(*trace_id, *span_id, trace_flags, *local_root_span_id, &[]); } } diff --git a/libdd-otel-thread-ctx/src/lib.rs b/libdd-otel-thread-ctx/src/lib.rs index cb1c1c5bab..029d7b4653 100644 --- a/libdd-otel-thread-ctx/src/lib.rs +++ b/libdd-otel-thread-ctx/src/lib.rs @@ -27,8 +27,8 @@ //! let local_root_span_id = [2u8; 8]; //! //! // First call allocates a record and attaches it. -//! ThreadContext::new(trace_id, span_id, local_root_span_id, &[(0, "first")]).attach(); -//! ThreadContext::update(trace_id, span_id, local_root_span_id, &[(0, "second")]); +//! ThreadContext::new(trace_id, span_id, 1, local_root_span_id, &[(0, "first")]).attach(); +//! ThreadContext::update(trace_id, span_id, 1, local_root_span_id, &[(0, "second")]); //! ThreadContext::detach(); //! # } //! # #[cfg(not(all(target_os = "linux", any(target_arch = "x86_64", target_arch = "aarch64"))))] @@ -52,7 +52,7 @@ //! let attrs: &[(u8, &str)] = &[(0, "GET"), (1, "/api/v1")]; //! //! // Publish a new context and save the previously attached one (if any). -//! let ctx = ThreadContext::new(trace_id, span_id, local_root_span_id, attrs); +//! let ctx = ThreadContext::new(trace_id, span_id, 1, local_root_span_id, attrs); //! let previous = ctx.attach(); //! //! // ... do work inside the span ... @@ -246,7 +246,8 @@ pub mod linux { /// Whether the record is ready/consistent. Always set to `1` except during in-place update /// of the current record. valid: AtomicU8, - _reserved: u8, + /// W3C Trace Context trace-flags byte associated with `trace_id` and `span_id`. + trace_flags: u8, /// Number of populated bytes in `attrs_data`. attrs_data_size: u16, /// Packed variable-length key-value records. @@ -271,7 +272,7 @@ pub mod linux { assert!(mem::offset_of!(ThreadContextRecord, trace_id) == 0); assert!(mem::offset_of!(ThreadContextRecord, span_id) == 16); assert!(mem::offset_of!(ThreadContextRecord, valid) == 24); - assert!(mem::offset_of!(ThreadContextRecord, _reserved) == 25); + assert!(mem::offset_of!(ThreadContextRecord, trace_flags) == 25); assert!(mem::offset_of!(ThreadContextRecord, attrs_data_size) == 26); assert!(mem::offset_of!(ThreadContextRecord, attrs_data) == 28); }; @@ -283,12 +284,14 @@ pub mod linux { fn new( trace_id: [u8; 16], span_id: [u8; 8], + trace_flags: u8, local_root_span_id: [u8; 8], attrs: &[(u8, &str)], ) -> Self { let mut record = Self { trace_id, span_id, + trace_flags, ..Default::default() }; record.set_attrs(local_root_span_id, attrs); @@ -371,7 +374,7 @@ pub mod linux { span_id: [0u8; 8], // We only ever set `valid` to `0` during in-place update of an attached context. valid: AtomicU8::new(1), - _reserved: 0, + trace_flags: 0, attrs_data_size: 0, attrs_data: [0u8; MAX_ATTRS_DATA_SIZE], } @@ -396,16 +399,19 @@ pub mod linux { pub struct ThreadContextHandle {} impl ThreadContext { - /// Create a new thread context with the given trace/span IDs and encoded attributes. + /// Create a new thread context with the given trace/span IDs, W3C trace-flags byte, and + /// encoded attributes. pub fn new( trace_id: [u8; 16], span_id: [u8; 8], + trace_flags: u8, local_root_span_id: [u8; 8], attrs: &[(u8, &str)], ) -> Self { Self::from(ThreadContextRecord::new( trace_id, span_id, + trace_flags, local_root_span_id, attrs, )) @@ -506,10 +512,13 @@ pub mod linux { /// outside that window. /// /// If there's currently no attached context, `update` will create one, and is in this case - /// equivalent to `ThreadContext::new(trace_id, span_id, attrs).attach()`. + /// equivalent to + /// `ThreadContext::new(trace_id, span_id, trace_flags, local_root_span_id, + /// attrs).attach()`. pub fn update( trace_id: [u8; 16], span_id: [u8; 8], + trace_flags: u8, local_root_span_id: [u8; 8], attrs: &[(u8, &str)], ) { @@ -523,14 +532,21 @@ pub mod linux { current.trace_id = trace_id; current.span_id = span_id; + current.trace_flags = trace_flags; current.set_attrs(local_root_span_id, attrs); compiler_fence(Ordering::SeqCst); current.valid.store(1, Ordering::Relaxed); } else { - let ctxt = ThreadContext::new(trace_id, span_id, local_root_span_id, attrs) - .into_ptr() - .as_ptr(); + let ctxt = ThreadContext::new( + trace_id, + span_id, + trace_flags, + local_root_span_id, + attrs, + ) + .into_ptr() + .as_ptr(); // No need for `AcqRel`, see [^tls-slot-ordering]. compiler_fence(Ordering::Release); // `ThreadContext::new` already initialises `valid = 1`. @@ -563,6 +579,8 @@ pub mod linux { use super::{ThreadContext, ThreadContextRecord}; use std::sync::atomic::Ordering; + const NO_TRACE_FLAGS: u8 = 0; + /// Read the TLS pointer for the current thread (the value stored in the TLS slot, not the /// address of the slot itself). fn read_tls_context_ptr() -> *const ThreadContextRecord { @@ -580,7 +598,7 @@ pub mod linux { read_tls_context_ptr().is_null(), "TLS must be null initially" ); - ThreadContext::new(trace_id, span_id, root_span_id, &[]).attach(); + ThreadContext::new(trace_id, span_id, NO_TRACE_FLAGS, root_span_id, &[]).attach(); assert!( !read_tls_context_ptr().is_null(), "TLS must not be null after attach" @@ -612,7 +630,7 @@ pub mod linux { let span_id = [2u8; 8]; let root_span_id = [3u8; 8]; - ThreadContext::new(trace_id, span_id, root_span_id, &[]).attach(); + ThreadContext::new(trace_id, span_id, NO_TRACE_FLAGS, root_span_id, &[]).attach(); let ptr = read_tls_context_ptr(); assert!(!ptr.is_null(), "TLS must be non-null after attach"); @@ -632,7 +650,7 @@ pub mod linux { #[cfg_attr(miri, ignore)] fn attribute_encoding_basic() { let attrs: &[(u8, &str)] = &[(1, "GET"), (2, "/api/v1")]; - ThreadContext::new([0u8; 16], [0u8; 8], [0u8; 8], attrs).attach(); + ThreadContext::new([0u8; 16], [0u8; 8], NO_TRACE_FLAGS, [0u8; 8], attrs).attach(); let ptr = read_tls_context_ptr(); assert!(!ptr.is_null()); @@ -672,7 +690,7 @@ pub mod linux { (3, val_c.as_str()), ]; - ThreadContext::new([0u8; 16], [0u8; 8], [0u8; 8], attrs).attach(); + ThreadContext::new([0u8; 16], [0u8; 8], NO_TRACE_FLAGS, [0u8; 8], attrs).attach(); let ptr = read_tls_context_ptr(); assert!(!ptr.is_null()); @@ -698,13 +716,14 @@ pub mod linux { let root_span_id2 = [0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80]; // Updating before any context is attached should be equivalent to `attach()` - ThreadContext::update(trace_id1, span_id1, root_span_id1, &[(0, "v1")]); + ThreadContext::update(trace_id1, span_id1, 0xA5, root_span_id1, &[(0, "v1")]); let ptr_before = read_tls_context_ptr(); assert!(!ptr_before.is_null()); let record = unsafe { &*ptr_before }; assert_eq!(record.trace_id, trace_id1); assert_eq!(record.span_id, span_id1); + assert_eq!(record.trace_flags, 0xA5); assert_eq!(record.valid.load(Ordering::Relaxed), 1); assert_eq!(record.attrs_data[0], 0); assert_eq!(record.attrs_data[1], 16); @@ -713,7 +732,7 @@ pub mod linux { assert_eq!(record.attrs_data[19], 2); assert_eq!(&record.attrs_data[20..22], b"v1"); - ThreadContext::update(trace_id2, span_id2, root_span_id2, &[(0, "v2")]); + ThreadContext::update(trace_id2, span_id2, 1, root_span_id2, &[(0, "v2")]); let ptr_after = read_tls_context_ptr(); assert_eq!( @@ -724,6 +743,7 @@ pub mod linux { let record = unsafe { &*ptr_after }; assert_eq!(record.trace_id, trace_id2); assert_eq!(record.span_id, span_id2); + assert_eq!(record.trace_flags, 1); assert_eq!(record.valid.load(Ordering::Relaxed), 1); assert_eq!(record.attrs_data[0], 0); assert_eq!(record.attrs_data[1], 16); @@ -739,7 +759,7 @@ pub mod linux { #[test] #[cfg_attr(miri, ignore)] fn explicit_detach_nulls_tls() { - ThreadContext::new([0u8; 16], [0u8; 8], [0u8; 8], &[]).attach(); + ThreadContext::new([0u8; 16], [0u8; 8], NO_TRACE_FLAGS, [0u8; 8], &[]).attach(); assert!(!read_tls_context_ptr().is_null()); let _ = ThreadContext::detach(); @@ -754,7 +774,14 @@ pub mod linux { #[cfg_attr(miri, ignore)] fn long_value_capped_at_255_bytes() { let long_val = "a".repeat(300); - ThreadContext::new([0u8; 16], [0u8; 8], [0u8; 8], &[(0, long_val.as_str())]).attach(); + ThreadContext::new( + [0u8; 16], + [0u8; 8], + NO_TRACE_FLAGS, + [0u8; 8], + &[(0, long_val.as_str())], + ) + .attach(); let ptr = read_tls_context_ptr(); assert!(!ptr.is_null()); @@ -785,8 +812,14 @@ pub mod linux { let main_root_span_id = [0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA]; let handle = std::thread::spawn(move || { - ThreadContext::new(spawned_trace_id, spawned_span_id, spawned_root_span_id, &[]) - .attach(); + ThreadContext::new( + spawned_trace_id, + spawned_span_id, + 0, + spawned_root_span_id, + &[], + ) + .attach(); // Let the main thread attach its own record and verify its slot. b.wait(); @@ -813,7 +846,7 @@ pub mod linux { "main thread should see a null pointer and not another thread's context" ); - ThreadContext::new(main_trace_id, main_span_id, main_root_span_id, &[]).attach(); + ThreadContext::new(main_trace_id, main_span_id, 0, main_root_span_id, &[]).attach(); let ptr = read_tls_context_ptr(); assert!(!ptr.is_null(), "main thread TLS must be set");