Skip to content

Commit af3a146

Browse files
committed
chore: root_span_id -> local_root_span_id: [u8; 8]
1 parent e6e8f80 commit af3a146

1 file changed

Lines changed: 69 additions & 66 deletions

File tree

libdd-profiling/src/otel_thread_ctx.rs

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ pub mod linux {
107107
}
108108
}
109109

110-
// We maintain the convention in libdatadog that the `root_span_id` attribute key is always the
111-
// very first in the string table, so its key index is guaranteed to be zero.
110+
// We maintain the convention in libdatadog that the `local_root_span_id` attribute key is
111+
// always the very first in the string table, so its key index is guaranteed to be zero.
112112
const ROOT_SPAN_KEY_INDEX: u8 = 0;
113113

114114
/// Maximum size in bytes of the `attrs_data` field.
@@ -169,13 +169,13 @@ pub mod linux {
169169
}
170170

171171
impl ThreadContextRecord {
172-
/// Build a record with the given trace id, span id and attributes. The `root_span_id` is a
173-
/// distinguished attribute with special handling for convenience, but it ends up as other
174-
/// attributes in `attrs_data`.
172+
/// Build a record with the given trace id, span id and attributes. The
173+
/// `local_root_span_id` is a distinguished attribute with special handling for
174+
/// convenience, but it ends up as other attributes in `attrs_data`.
175175
fn new(
176176
trace_id: [u8; 16],
177177
span_id: [u8; 8],
178-
root_span_id: &str,
178+
local_root_span_id: [u8; 8],
179179
attrs: &[(u8, &str)],
180180
) -> Self {
181181
const { assert!(size_of::<ThreadContextRecord>() == 640) }
@@ -185,7 +185,7 @@ pub mod linux {
185185
span_id,
186186
..Default::default()
187187
};
188-
record.set_attrs(root_span_id, attrs);
188+
record.set_attrs(local_root_span_id, attrs);
189189
record
190190
}
191191

@@ -210,13 +210,15 @@ pub mod linux {
210210
/// recovery would require us to be able to rollback to the previous attributes which would
211211
/// hurt the happy path, or leave the record in a inconsistent state. Another possibility
212212
/// would be to error out and reset the record in that situation.
213-
fn set_attrs(&mut self, root_span_id: &str, attributes: &[(u8, &str)]) -> bool {
214-
let mut offset = 0;
213+
fn set_attrs(&mut self, local_root_span_id: [u8; 8], attributes: &[(u8, &str)]) -> bool {
215214
let mut fully_encoded = true;
216-
let root_span_attr = (ROOT_SPAN_KEY_INDEX, root_span_id);
217-
let full_attrs = std::iter::once(&root_span_attr).chain(attributes);
218215

219-
for &(key_index, val) in full_attrs {
216+
self.attrs_data[0] = ROOT_SPAN_KEY_INDEX;
217+
self.attrs_data[1] = 8;
218+
self.attrs_data[2..10].copy_from_slice(local_root_span_id.as_slice());
219+
let mut offset = 10;
220+
221+
for &(key_index, val) in attributes {
220222
let val_bytes = val.as_bytes();
221223
let val_len = val_bytes.len();
222224
let val_len = if val_len > 255 {
@@ -277,13 +279,13 @@ pub mod linux {
277279
pub fn new(
278280
trace_id: [u8; 16],
279281
span_id: [u8; 8],
280-
root_span_id: &str,
282+
local_root_span_id: [u8; 8],
281283
attrs: &[(u8, &str)],
282284
) -> Self {
283285
Self::from(ThreadContextRecord::new(
284286
trace_id,
285287
span_id,
286-
root_span_id,
288+
local_root_span_id,
287289
attrs,
288290
))
289291
}
@@ -361,7 +363,7 @@ pub mod linux {
361363
pub fn update(
362364
trace_id: [u8; 16],
363365
span_id: [u8; 8],
364-
root_span_id: &str,
366+
local_root_span_id: [u8; 8],
365367
attrs: &[(u8, &str)],
366368
) {
367369
let slot = get_tls_slot();
@@ -372,7 +374,7 @@ pub mod linux {
372374

373375
current.trace_id = trace_id;
374376
current.span_id = span_id;
375-
current.set_attrs(root_span_id, attrs);
377+
current.set_attrs(local_root_span_id, attrs);
376378

377379
compiler_fence(Ordering::SeqCst);
378380
current.valid.store(1, Ordering::Relaxed);
@@ -382,7 +384,7 @@ pub mod linux {
382384
// `ThreadContext::new` already initialises `valid = 1`.
383385
let _ = Self::swap(
384386
slot,
385-
ThreadContext::new(trace_id, span_id, root_span_id, attrs).into_raw(),
387+
ThreadContext::new(trace_id, span_id, local_root_span_id, attrs).into_raw(),
386388
);
387389
}
388390
}
@@ -422,12 +424,13 @@ pub mod linux {
422424
fn tls_lifecycle_basic() {
423425
let trace_id = [1u8; 16];
424426
let span_id = [2u8; 8];
427+
let root_span_id = [3u8; 8];
425428

426429
assert!(
427430
read_tls_context_ptr().is_null(),
428431
"TLS must be null initially"
429432
);
430-
ThreadContext::new(trace_id, span_id, "", &[]).attach();
433+
ThreadContext::new(trace_id, span_id, root_span_id, &[]).attach();
431434
assert!(
432435
!read_tls_context_ptr().is_null(),
433436
"TLS must not be null after attach"
@@ -457,8 +460,9 @@ pub mod linux {
457460
fn raw_tls_pointer_read() {
458461
let trace_id = [1u8; 16];
459462
let span_id = [2u8; 8];
463+
let root_span_id = [3u8; 8];
460464

461-
ThreadContext::new(trace_id, span_id, "", &[]).attach();
465+
ThreadContext::new(trace_id, span_id, root_span_id, &[]).attach();
462466

463467
let ptr = read_tls_context_ptr();
464468
assert!(!ptr.is_null(), "TLS must be non-null after attach");
@@ -468,32 +472,34 @@ pub mod linux {
468472
assert_eq!(record.trace_id, trace_id);
469473
assert_eq!(record.span_id, span_id);
470474
assert_eq!(record.valid.load(Ordering::Relaxed), 1);
471-
assert_eq!(record.attrs_data_size, 2);
475+
// 1 (key) + 1 (len) + 8 (root_span_id bytes) = 10
476+
assert_eq!(record.attrs_data_size, 10);
472477

473478
let _ = ThreadContext::detach();
474479
}
475480

476481
#[test]
477482
#[cfg_attr(miri, ignore)]
478483
fn attribute_encoding_basic() {
479-
let root_span_id = "aaaa-bbbb";
484+
let root_span_id = [0u8; 8];
480485
let attrs: &[(u8, &str)] = &[(1, "GET"), (2, "/api/v1")];
481486
ThreadContext::new([0u8; 16], [0u8; 8], root_span_id, attrs).attach();
482487

483488
let ptr = read_tls_context_ptr();
484489
assert!(!ptr.is_null());
485490
let record = unsafe { &*ptr };
486-
let expected_size: u16 = (2 + 9 + 2 + 3 + 2 + 7) as u16;
491+
// 1+1+8 (root_span_id) + 1+1+3 (GET) + 1+1+7 (/api/v1)
492+
let expected_size: u16 = (2 + 8 + 2 + 3 + 2 + 7) as u16;
487493
assert_eq!(record.attrs_data_size, expected_size);
488494
assert_eq!(record.attrs_data[0], 0);
489-
assert_eq!(record.attrs_data[1], 9);
490-
assert_eq!(&record.attrs_data[2..11], b"aaaa-bbbb");
491-
assert_eq!(record.attrs_data[11], 1);
492-
assert_eq!(record.attrs_data[12], 3);
493-
assert_eq!(&record.attrs_data[13..16], b"GET");
494-
assert_eq!(record.attrs_data[16], 2);
495-
assert_eq!(record.attrs_data[17], 7);
496-
assert_eq!(&record.attrs_data[18..25], b"/api/v1");
495+
assert_eq!(record.attrs_data[1], 8);
496+
assert_eq!(&record.attrs_data[2..10], &root_span_id);
497+
assert_eq!(record.attrs_data[10], 1);
498+
assert_eq!(record.attrs_data[11], 3);
499+
assert_eq!(&record.attrs_data[12..15], b"GET");
500+
assert_eq!(record.attrs_data[15], 2);
501+
assert_eq!(record.attrs_data[16], 7);
502+
assert_eq!(&record.attrs_data[17..24], b"/api/v1");
497503

498504
let _ = ThreadContext::detach();
499505
}
@@ -503,30 +509,31 @@ pub mod linux {
503509
fn attribute_truncation_on_overflow() {
504510
// Build attributes whose combined encoded size exceeds MAX_ATTRS_DATA_SIZE.
505511
// Each max entry: 1 (key) + 1 (len) + 255 (val) = 257 bytes.
506-
// Two such entries: 514 bytes, plus an empty root_span_id: 516.
507-
// A third entry of 100 chars would need 102 bytes, bringing the total to 618 > 612, so
512+
// root_span_id: 1 (key) + 1 (len) + 8 (val) = 10 bytes.
513+
// Two such entries: 514 bytes, plus root_span_id: 524.
514+
// A third entry of 100 chars would need 102 bytes, bringing the total to 626 > 612, so
508515
// the third entry must be dropped.
509516
let val_a = "a".repeat(255); // 257 bytes encoded
510517
let val_b = "b".repeat(255); // 257 bytes encoded → 514 total
511-
let val_c = "c".repeat(100); // 102 bytes encoded → 616 total: must be dropped
518+
let val_c = "c".repeat(100); // 102 bytes encoded → 626 total: must be dropped
512519

513520
let attrs: &[(u8, &str)] = &[
514521
(1, val_a.as_str()),
515522
(2, val_b.as_str()),
516523
(3, val_c.as_str()),
517524
];
518525

519-
ThreadContext::new([0u8; 16], [0u8; 8], "", attrs).attach();
526+
ThreadContext::new([0u8; 16], [0u8; 8], [0u8; 8], attrs).attach();
520527

521528
let ptr = read_tls_context_ptr();
522529
assert!(!ptr.is_null());
523530
let record = unsafe { &*ptr };
524-
// Only the first two entries fit (514 bytes + 2 bytes for empty root span id).
525-
assert_eq!(record.attrs_data_size, 516);
526-
assert_eq!(record.attrs_data[2], 1);
527-
assert_eq!(record.attrs_data[3], 255);
528-
assert_eq!(record.attrs_data[259], 2);
529-
assert_eq!(record.attrs_data[260], 255);
531+
// Only the first two entries fit (514 bytes + 10 bytes for root_span_id).
532+
assert_eq!(record.attrs_data_size, 524);
533+
assert_eq!(record.attrs_data[10], 1);
534+
assert_eq!(record.attrs_data[11], 255);
535+
assert_eq!(record.attrs_data[267], 2);
536+
assert_eq!(record.attrs_data[268], 255);
530537

531538
let _ = ThreadContext::detach();
532539
}
@@ -536,10 +543,10 @@ pub mod linux {
536543
fn update_record_in_place() {
537544
let trace_id1 = [1u8; 16];
538545
let span_id1 = [1u8; 8];
539-
let root_span_id1 = "xxxx";
546+
let root_span_id1 = [0x78u8; 8];
540547
let trace_id2 = [2u8; 16];
541548
let span_id2 = [2u8; 8];
542-
let root_span_id2 = "yyyy";
549+
let root_span_id2 = [0x79u8; 8];
543550

544551
// Updating before any context is attached should be equivalent to `attach()`
545552
ThreadContext::update(trace_id1, span_id1, root_span_id1, &[(0, "v1")]);
@@ -551,11 +558,11 @@ pub mod linux {
551558
assert_eq!(record.span_id, span_id1);
552559
assert_eq!(record.valid.load(Ordering::Relaxed), 1);
553560
assert_eq!(record.attrs_data[0], 0);
554-
assert_eq!(record.attrs_data[1], 4);
555-
assert_eq!(&record.attrs_data[2..6], root_span_id1.as_bytes());
556-
assert_eq!(record.attrs_data[6], 0);
557-
assert_eq!(record.attrs_data[7], 2);
558-
assert_eq!(&record.attrs_data[8..10], b"v1");
561+
assert_eq!(record.attrs_data[1], 8);
562+
assert_eq!(&record.attrs_data[2..10], &root_span_id1);
563+
assert_eq!(record.attrs_data[10], 0);
564+
assert_eq!(record.attrs_data[11], 2);
565+
assert_eq!(&record.attrs_data[12..14], b"v1");
559566

560567
ThreadContext::update(trace_id2, span_id2, root_span_id2, &[(0, "v2")]);
561568

@@ -570,11 +577,11 @@ pub mod linux {
570577
assert_eq!(record.span_id, span_id2);
571578
assert_eq!(record.valid.load(Ordering::Relaxed), 1);
572579
assert_eq!(record.attrs_data[0], 0);
573-
assert_eq!(record.attrs_data[1], 4);
574-
assert_eq!(&record.attrs_data[2..6], root_span_id2.as_bytes());
575-
assert_eq!(record.attrs_data[6], 0);
576-
assert_eq!(record.attrs_data[7], 2);
577-
assert_eq!(&record.attrs_data[8..10], b"v2");
580+
assert_eq!(record.attrs_data[1], 8);
581+
assert_eq!(&record.attrs_data[2..10], &root_span_id2);
582+
assert_eq!(record.attrs_data[10], 0);
583+
assert_eq!(record.attrs_data[11], 2);
584+
assert_eq!(&record.attrs_data[12..14], b"v2");
578585

579586
let _ = ThreadContext::detach();
580587
assert!(read_tls_context_ptr().is_null());
@@ -583,7 +590,7 @@ pub mod linux {
583590
#[test]
584591
#[cfg_attr(miri, ignore)]
585592
fn explicit_detach_nulls_tls() {
586-
ThreadContext::new([3u8; 16], [3u8; 8], "aaaa", &[]).attach();
593+
ThreadContext::new([0u8; 16], [0u8; 8], [0u8; 8], &[]).attach();
587594
assert!(!read_tls_context_ptr().is_null());
588595

589596
let _ = ThreadContext::detach();
@@ -598,14 +605,16 @@ pub mod linux {
598605
#[cfg_attr(miri, ignore)]
599606
fn long_value_capped_at_255_bytes() {
600607
let long_val = "a".repeat(300);
601-
ThreadContext::new([0u8; 16], [0u8; 8], "bbbb", &[(0, long_val.as_str())]).attach();
608+
ThreadContext::new([0u8; 16], [0u8; 8], [0u8; 8], &[(0, long_val.as_str())]).attach();
602609

603610
let ptr = read_tls_context_ptr();
604611
assert!(!ptr.is_null());
605612
let record = unsafe { &*ptr };
606-
let val_len = record.attrs_data[2 + 4 + 1];
613+
// root_span_id occupies offset 0..10, then the attr entry starts at 10: key at [10],
614+
// len at [11]
615+
let val_len = record.attrs_data[2 + 8 + 1];
607616
assert_eq!(val_len, 255, "value must be capped at 255 bytes");
608-
assert_eq!(record.attrs_data_size, 2 + 4 + 2 + 255);
617+
assert_eq!(record.attrs_data_size, 2 + 8 + 2 + 255);
609618

610619
let _ = ThreadContext::detach();
611620
}
@@ -621,10 +630,10 @@ pub mod linux {
621630

622631
let spawned_trace_id = [0xABu8; 16];
623632
let spawned_span_id = [0xCDu8; 8];
624-
let spawned_root_span_id = "xxxx";
633+
let spawned_root_span_id = [0xEFu8; 8];
625634
let main_trace_id = [0x11u8; 16];
626635
let main_span_id = [0x22u8; 8];
627-
let main_root_span_id = "yyyy";
636+
let main_root_span_id = [0x33u8; 8];
628637

629638
let handle = std::thread::spawn(move || {
630639
ThreadContext::new(spawned_trace_id, spawned_span_id, spawned_root_span_id, &[])
@@ -641,10 +650,7 @@ pub mod linux {
641650
let record = unsafe { &*ptr };
642651
assert_eq!(record.trace_id, spawned_trace_id);
643652
assert_eq!(record.span_id, spawned_span_id);
644-
assert_eq!(
645-
&record.attrs_data[2..(record.attrs_data_size as usize)],
646-
spawned_root_span_id.as_bytes()
647-
);
653+
assert_eq!(&record.attrs_data[2..10], &spawned_root_span_id);
648654

649655
let _ = ThreadContext::detach();
650656
assert!(read_tls_context_ptr().is_null());
@@ -665,10 +671,7 @@ pub mod linux {
665671
let record = unsafe { &*ptr };
666672
assert_eq!(record.trace_id, main_trace_id);
667673
assert_eq!(record.span_id, main_span_id);
668-
assert_eq!(
669-
&record.attrs_data[2..(record.attrs_data_size as usize)],
670-
main_root_span_id.as_bytes()
671-
);
674+
assert_eq!(&record.attrs_data[2..10], &main_root_span_id);
672675

673676
barrier.wait();
674677

0 commit comments

Comments
 (0)