Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions libdd-otel-thread-ctx-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,19 @@ 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`.
#[no_mangle]
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<ThreadContextHandle> {
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.
Expand Down Expand Up @@ -93,16 +95,17 @@ 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`.
#[no_mangle]
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, &[]);
}
}
77 changes: 55 additions & 22 deletions libdd-otel-thread-ctx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))))]
Expand All @@ -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 ...
Expand Down Expand Up @@ -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.
Expand All @@ -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);
};
Expand All @@ -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);
Expand Down Expand Up @@ -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],
}
Expand All @@ -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,
))
Expand Down Expand Up @@ -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)],
) {
Expand All @@ -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`.
Expand Down Expand Up @@ -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 {
Expand All @@ -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"
Expand Down Expand Up @@ -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");
Expand All @@ -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());
Expand Down Expand Up @@ -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());
Expand All @@ -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);
Expand All @@ -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!(
Expand All @@ -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);
Expand All @@ -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();
Expand All @@ -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());
Expand Down Expand Up @@ -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();
Expand All @@ -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");
Expand Down
Loading