Skip to content

Commit fea2bb5

Browse files
jbachorikclaude
andcommitted
Address review feedback: fix races, remove dead code, clean up tests
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 349ebbc commit fea2bb5

10 files changed

Lines changed: 34 additions & 333 deletions

File tree

ddprof-lib/src/main/cpp/context.cpp

Lines changed: 0 additions & 17 deletions
This file was deleted.

ddprof-lib/src/main/cpp/context_api.cpp

Lines changed: 6 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -27,56 +27,23 @@
2727
char* ContextApi::_attribute_keys[MAX_ATTRIBUTE_KEYS] = {};
2828
int ContextApi::_attribute_key_count = 0;
2929

30-
void ContextApi::initialize() {
31-
// No per-session state to initialize — OTEL TLS is always used.
32-
}
33-
3430
void ContextApi::shutdown() {
3531
freeAttributeKeys();
3632
}
3733

3834
/**
3935
* Initialize OTel TLS for the current thread on first use.
40-
* Zero-inits the embedded OtelThreadContextRecord and triggers TLS init.
36+
* Triggers the first access to custom_labels_current_set_v2 (TLS init).
4137
* Must be called with signals blocked to prevent musl TLS deadlock.
38+
* The OtelThreadContextRecord is already zero-initialized by the ProfiledThread ctor.
4239
*/
43-
static void initializeOtelTls(ProfiledThread* thrd) {
40+
void ContextApi::initializeOtelTls(ProfiledThread* thrd) {
4441
SignalBlocker blocker;
45-
46-
OtelThreadContextRecord* record = thrd->getOtelContextRecord();
47-
record->valid = 0;
48-
record->_reserved = 0;
49-
record->attrs_data_size = 0;
50-
5142
// First access to custom_labels_current_set_v2 triggers TLS init
5243
custom_labels_current_set_v2 = nullptr;
53-
5444
thrd->markOtelContextInitialized();
5545
}
5646

57-
void ContextApi::setFull(u64 local_root_span_id, u64 span_id, u64 trace_id_high, u64 trace_id_low) {
58-
ProfiledThread* thrd = ProfiledThread::current();
59-
if (thrd == nullptr) return;
60-
61-
if (!thrd->isOtelContextInitialized()) {
62-
initializeOtelTls(thrd);
63-
}
64-
65-
// All-zero IDs = context detachment
66-
if (trace_id_high == 0 && trace_id_low == 0 && span_id == 0) {
67-
OtelContexts::clear(thrd->getOtelContextRecord());
68-
thrd->clearOtelSidecar();
69-
return;
70-
}
71-
72-
// Update sidecar BEFORE publishing the OTEP record. OtelContexts::set()
73-
// flips valid=1 at the end; a signal handler that sees valid=1 must also
74-
// see the updated local_root_span_id.
75-
thrd->setOtelLocalRootSpanId(local_root_span_id);
76-
77-
OtelContexts::set(thrd->getOtelContextRecord(), trace_id_high, trace_id_low, span_id, local_root_span_id);
78-
}
79-
8047
bool ContextApi::get(u64& span_id, u64& root_span_id) {
8148
ProfiledThread* thrd = ProfiledThread::currentSignalSafe();
8249
if (thrd == nullptr || !thrd->isOtelContextInitialized()) {
@@ -100,41 +67,13 @@ Context ContextApi::snapshot() {
10067
return thrd->snapshotContext(numAttrs);
10168
}
10269

103-
bool ContextApi::setAttribute(uint8_t key_index, const char* value, uint8_t value_len) {
104-
if (key_index >= DD_TAGS_CAPACITY) return false;
105-
106-
ProfiledThread* thrd = ProfiledThread::current();
107-
if (thrd == nullptr) return false;
108-
109-
if (!thrd->isOtelContextInitialized()) {
110-
initializeOtelTls(thrd);
111-
}
112-
113-
// Pre-register the value string in the Dictionary and cache the encoding
114-
// in the sidecar for O(1) signal-handler reads (no hash lookup needed).
115-
u32 encoding = Profiler::instance()->contextValueMap()->bounded_lookup(
116-
value, value_len, 1 << 16);
117-
if (encoding == INT_MAX) {
118-
thrd->setOtelTagEncoding(key_index, 0);
119-
return false;
120-
}
121-
thrd->setOtelTagEncoding(key_index, encoding);
122-
123-
// Offset by 1 in the OTEP record: index 0 is reserved for LOCAL_ROOT_SPAN_ATTR_INDEX
124-
uint8_t otep_key_index = key_index + 1;
125-
return OtelContexts::setAttribute(thrd->getOtelContextRecord(), otep_key_index, value, value_len);
126-
}
127-
12870
void ContextApi::freeAttributeKeys() {
12971
int count = _attribute_key_count;
130-
char* old_keys[MAX_ATTRIBUTE_KEYS];
131-
for (int i = 0; i < count; i++) {
132-
old_keys[i] = _attribute_keys[i];
133-
_attribute_keys[i] = nullptr;
134-
}
13572
_attribute_key_count = 0;
13673
for (int i = 0; i < count; i++) {
137-
free(old_keys[i]);
74+
char* key = _attribute_keys[i];
75+
_attribute_keys[i] = nullptr;
76+
free(key);
13877
}
13978
}
14079

ddprof-lib/src/main/cpp/context_api.h

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include "context.h"
2222
#include <cstdint>
2323

24+
class ProfiledThread;
25+
2426
/**
2527
* Unified context API for trace/span context storage.
2628
*
@@ -30,30 +32,17 @@
3032
*/
3133
class ContextApi {
3234
public:
33-
/**
34-
* Initialize context storage.
35-
* Must be called once during profiler startup.
36-
*/
37-
static void initialize();
38-
3935
/**
4036
* Shutdown context storage.
4137
* Releases resources allocated during initialization.
4238
*/
4339
static void shutdown();
4440

4541
/**
46-
* Write full OTEL context with 128-bit trace ID and local root span ID.
47-
*
48-
* Writes trace_id + span_id + local_root_span_id to the OTEP record
49-
* in a single detach/attach cycle.
50-
*
51-
* @param local_root_span_id Local root span ID (for endpoint correlation)
52-
* @param span_id The span ID
53-
* @param trace_id_high Upper 64 bits of 128-bit trace ID
54-
* @param trace_id_low Lower 64 bits of 128-bit trace ID
42+
* Initialize OTel TLS for the given thread on first use.
43+
* Must be called with signals blocked (SignalBlocker).
5544
*/
56-
static void setFull(u64 local_root_span_id, u64 span_id, u64 trace_id_high, u64 trace_id_low);
45+
static void initializeOtelTls(ProfiledThread* thrd);
5746

5847
/**
5948
* Read context for the current thread.
@@ -78,19 +67,6 @@ class ContextApi {
7867
*/
7968
static Context snapshot();
8069

81-
/**
82-
* Set a custom attribute on the current thread's context.
83-
*
84-
* Encodes into attrs_data of the OTEP record and caches the
85-
* encoding in the ProfiledThread sidecar for O(1) signal-handler reads.
86-
*
87-
* @param key_index Index into the registered attribute key map
88-
* @param value UTF-8 string value
89-
* @param value_len Length of value in bytes (max 255)
90-
* @return true if the attribute was set successfully
91-
*/
92-
static bool setAttribute(uint8_t key_index, const char* value, uint8_t value_len);
93-
9470
/**
9571
* Register attribute key names and publish them in the process context.
9672
* Must be called before setAttribute().

ddprof-lib/src/main/cpp/javaApi.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -549,14 +549,7 @@ Java_com_datadoghq_profiler_JavaProfiler_initializeOtelTls0(JNIEnv* env, jclass
549549
if (thrd == nullptr) return nullptr;
550550

551551
if (!thrd->isOtelContextInitialized()) {
552-
// Block profiling signals during first TLS access
553-
SignalBlocker blocker;
554-
OtelThreadContextRecord* record = thrd->getOtelContextRecord();
555-
record->valid = 0;
556-
record->_reserved = 0;
557-
record->attrs_data_size = 0;
558-
custom_labels_current_set_v2 = nullptr;
559-
thrd->markOtelContextInitialized();
552+
ContextApi::initializeOtelTls(thrd);
560553
}
561554

562555
OtelThreadContextRecord* record = thrd->getOtelContextRecord();

ddprof-lib/src/main/cpp/otel_context.cpp

Lines changed: 0 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -35,77 +35,6 @@ u64 OtelContexts::bytesToU64(const uint8_t* in) {
3535
return val;
3636
}
3737

38-
void OtelContexts::set(OtelThreadContextRecord* record, u64 trace_id_high, u64 trace_id_low, u64 span_id, u64 local_root_span_id) {
39-
if (record == nullptr) {
40-
return;
41-
}
42-
43-
// OTEP publication protocol:
44-
// 1. Detach — external readers see no record during construction
45-
__atomic_store_n(&custom_labels_current_set_v2, (OtelThreadContextRecord*)nullptr, __ATOMIC_RELEASE);
46-
47-
// 2. Invalidate — internal readers (signal handlers using cached pointer) see invalid record
48-
__atomic_store_n(&record->valid, (uint8_t)0, __ATOMIC_RELEASE);
49-
50-
// 3. Populate record fields
51-
u64ToBytes(trace_id_high, record->trace_id);
52-
u64ToBytes(trace_id_low, record->trace_id + 8);
53-
u64ToBytes(span_id, record->span_id);
54-
55-
// 4. Rebuild attrs_data: preserve user entries (key_index != 0), replace LRS entry.
56-
// LOCAL_ROOT_SPAN_ATTR_INDEX = 0 is reserved; user attrs use key_index >= 1.
57-
// Use a temp buffer because source and destination regions overlap.
58-
uint16_t old_size = record->attrs_data_size;
59-
uint8_t user_buf[OTEL_MAX_ATTRS_DATA_SIZE];
60-
uint16_t user_size = 0;
61-
62-
uint16_t pos = 0;
63-
while (pos + 2 <= old_size) {
64-
uint8_t k = record->attrs_data[pos];
65-
uint8_t len = record->attrs_data[pos + 1];
66-
if (pos + 2 + len > old_size) break;
67-
if (k != 0) {
68-
memcpy(user_buf + user_size, record->attrs_data + pos, 2 + len);
69-
user_size += 2 + len;
70-
}
71-
pos += 2 + len;
72-
}
73-
74-
uint16_t new_size = 0;
75-
76-
// Write local_root_span_id as decimal string attribute at reserved index 0
77-
if (local_root_span_id != 0) {
78-
// Max digits for u64: 20 ("18446744073709551615"). Entry = 2 + digits.
79-
uint8_t digits[20];
80-
int ndigits = 0;
81-
u64 v = local_root_span_id;
82-
do {
83-
digits[ndigits++] = '0' + (v % 10);
84-
v /= 10;
85-
} while (v > 0);
86-
record->attrs_data[0] = 0; // key_index = LOCAL_ROOT_SPAN_ATTR_INDEX
87-
record->attrs_data[1] = ndigits; // length
88-
for (int i = 0; i < ndigits; i++) {
89-
record->attrs_data[2 + i] = digits[ndigits - 1 - i]; // reverse into big-endian order
90-
}
91-
new_size = 2 + ndigits;
92-
}
93-
94-
// Re-append preserved user entries
95-
if (user_size > 0 && new_size + user_size <= OTEL_MAX_ATTRS_DATA_SIZE) {
96-
memcpy(record->attrs_data + new_size, user_buf, user_size);
97-
new_size += user_size;
98-
}
99-
100-
record->attrs_data_size = new_size;
101-
102-
// 5. Mark record valid
103-
__atomic_store_n(&record->valid, (uint8_t)1, __ATOMIC_RELEASE);
104-
105-
// 6. Attach — publish completed record
106-
__atomic_store_n(&custom_labels_current_set_v2, record, __ATOMIC_RELEASE);
107-
}
108-
10938
bool OtelContexts::getSpanId(OtelThreadContextRecord* record, u64& span_id) {
11039
if (record == nullptr) {
11140
return false;
@@ -117,67 +46,3 @@ bool OtelContexts::getSpanId(OtelThreadContextRecord* record, u64& span_id) {
11746
return true;
11847
}
11948

120-
bool OtelContexts::setAttribute(OtelThreadContextRecord* record, uint8_t key_index, const char* value, uint8_t value_len) {
121-
if (record == nullptr) {
122-
return false;
123-
}
124-
125-
// Entry size: 1 (key_index) + 1 (length) + value_len
126-
uint16_t entry_size = 2 + value_len;
127-
uint16_t current_size = record->attrs_data_size;
128-
129-
// Detach (external readers) and invalidate (signal handler readers)
130-
__atomic_store_n(&custom_labels_current_set_v2, (OtelThreadContextRecord*)nullptr, __ATOMIC_RELEASE);
131-
__atomic_store_n(&record->valid, (uint8_t)0, __ATOMIC_RELEASE);
132-
133-
// Scan for duplicate key and compact if found
134-
uint16_t read_pos = 0;
135-
uint16_t write_pos = 0;
136-
bool found = false;
137-
while (read_pos + 2 <= current_size) {
138-
uint8_t k = record->attrs_data[read_pos];
139-
uint8_t len = record->attrs_data[read_pos + 1];
140-
if (read_pos + 2 + len > current_size) break; // corrupt length guard
141-
if (k == key_index) {
142-
found = true;
143-
read_pos += 2 + len;
144-
} else {
145-
if (found && write_pos < read_pos) {
146-
memmove(record->attrs_data + write_pos, record->attrs_data + read_pos, 2 + len);
147-
}
148-
write_pos += 2 + len;
149-
read_pos += 2 + len;
150-
}
151-
}
152-
if (found) {
153-
current_size = write_pos;
154-
}
155-
156-
// Re-check fit after compaction
157-
if (current_size + entry_size > OTEL_MAX_ATTRS_DATA_SIZE) {
158-
record->attrs_data_size = current_size;
159-
__atomic_store_n(&record->valid, (uint8_t)1, __ATOMIC_RELEASE);
160-
__atomic_store_n(&custom_labels_current_set_v2, record, __ATOMIC_RELEASE);
161-
return false;
162-
}
163-
164-
// Append the new entry
165-
record->attrs_data[current_size] = key_index;
166-
record->attrs_data[current_size + 1] = value_len;
167-
memcpy(record->attrs_data + current_size + 2, value, value_len);
168-
record->attrs_data_size = current_size + entry_size;
169-
170-
// Re-publish
171-
__atomic_store_n(&record->valid, (uint8_t)1, __ATOMIC_RELEASE);
172-
__atomic_store_n(&custom_labels_current_set_v2, record, __ATOMIC_RELEASE);
173-
return true;
174-
}
175-
176-
void OtelContexts::clear(OtelThreadContextRecord* record) {
177-
if (record != nullptr) {
178-
__atomic_store_n(&record->valid, (uint8_t)0, __ATOMIC_RELEASE);
179-
record->attrs_data_size = 0;
180-
}
181-
// OTEP context detachment: set TLS pointer to nullptr
182-
__atomic_store_n(&custom_labels_current_set_v2, (OtelThreadContextRecord*)nullptr, __ATOMIC_RELEASE);
183-
}

ddprof-lib/src/main/cpp/otel_context.h

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,38 +73,13 @@ DLLEXPORT extern thread_local OtelThreadContextRecord* custom_labels_current_set
7373
*/
7474
class OtelContexts {
7575
public:
76-
/**
77-
* Write context for the current thread using OTEP publication protocol:
78-
* 1. Set TLS pointer to nullptr (detach — external readers)
79-
* 2. Set valid = 0 (invalidate — signal handler readers via cached pointer)
80-
* 3. Populate record fields (trace_id, span_id, local_root_span_id as hex attr)
81-
* 4. Set valid = 1
82-
* 5. Set TLS pointer to record (attach)
83-
*
84-
* local_root_span_id is encoded as a 16-char hex string attribute at index 0.
85-
* Existing user attributes (key_index >= 1) are preserved across calls.
86-
*/
87-
static void set(OtelThreadContextRecord* record, u64 trace_id_high, u64 trace_id_low, u64 span_id, u64 local_root_span_id);
88-
8976
/**
9077
* Read span_id from a record pointer (signal-safe).
91-
* Skips trace_id decode for efficiency.
92-
* Returns false if the record is null or invalid.
78+
* Checks the valid flag with acquire semantics before reading.
79+
* Returns false if the record is null or invalid (being mutated).
9380
*/
9481
static bool getSpanId(OtelThreadContextRecord* record, u64& span_id);
9582

96-
/**
97-
* Write a single attribute into the current thread's OTEP record.
98-
* Appends (key_index, length, value) to attrs_data, re-publishes via detach/attach.
99-
* Returns false if the attribute doesn't fit in the remaining space.
100-
*/
101-
static bool setAttribute(OtelThreadContextRecord* record, uint8_t key_index, const char* value, uint8_t value_len);
102-
103-
/**
104-
* Clear context for the current thread (set TLS pointer to nullptr).
105-
*/
106-
static void clear(OtelThreadContextRecord* record);
107-
10883
// Byte conversion helpers (big-endian, W3C trace context)
10984
static void u64ToBytes(u64 val, uint8_t* out);
11085
static u64 bytesToU64(const uint8_t* in);

ddprof-lib/src/main/cpp/profiler.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,9 +1440,6 @@ Error Profiler::start(Arguments &args, bool reset) {
14401440
_libs->updateBuildIds();
14411441
}
14421442

1443-
// Initialize OTEL context storage
1444-
ContextApi::initialize();
1445-
14461443
enableEngines();
14471444

14481445
// Always enable library trap to catch wasmtime loading and patch its broken sigaction

0 commit comments

Comments
 (0)