Skip to content

Commit c372c7a

Browse files
author
elsa
committed
feat: add data in tls for trace to profile correlation
* BUILD.bazel * include/datadog/tls_storage.h * include/datadog/trace_segment.h * include/datadog/tracer.h * src/datadog/span.cpp * src/datadog/trace_segment.cpp * src/datadog/tracer.cpp
1 parent 9bfc79b commit c372c7a

File tree

7 files changed

+97
-0
lines changed

7 files changed

+97
-0
lines changed

BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ cc_library(
113113
"include/datadog/span_matcher.h",
114114
"include/datadog/span_sampler_config.h",
115115
"include/datadog/string_view.h",
116+
"include/datadog/tls_storage.h",
116117
"include/datadog/tracer.h",
117118
"include/datadog/tracer_config.h",
118119
"include/datadog/tracer_signature.h",

include/datadog/tls_storage.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include <datadog/trace_id.h>
4+
5+
#include <array>
6+
#include <cstdint>
7+
8+
// Global struct used to exposed thread-specific information.
9+
// https://github.com/elastic/apm/blob/149cd3e39a77a58002344270ed2ad35357bdd02d/specs/agents/universal-profiling-integration.md#thread-local-storage-layout
10+
11+
namespace datadog {
12+
namespace tracing {
13+
struct __attribute__((packed)) TLSStorage {
14+
uint16_t layout_minor_version;
15+
uint8_t valid;
16+
uint8_t trace_present;
17+
uint8_t trace_flags;
18+
uint64_t trace_id_low;
19+
uint64_t trace_id_high;
20+
uint64_t span_id;
21+
uint64_t transaction_id;
22+
};
23+
24+
} // namespace tracing
25+
} // namespace datadog

include/datadog/trace_segment.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class TraceSegment {
102102
const Optional<std::string>& origin() const;
103103
Optional<SamplingDecision> sampling_decision() const;
104104

105+
uint64_t local_root_id() const;
106+
105107
Logger& logger() const;
106108

107109
// Inject trace context for the specified `span` into the specified `writer`.

include/datadog/tracer.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
// obtained from a `TracerConfig` via the `finalize_config` function. See
1111
// `tracer_config.h`.
1212

13+
#ifdef __linux__
14+
#include <datadog/tls_storage.h>
15+
#endif
16+
1317
#include <cstddef>
1418
#include <memory>
1519

@@ -25,6 +29,8 @@
2529

2630
#ifdef __linux__
2731
extern const void* elastic_apm_profiling_correlation_process_storage_v1;
32+
extern thread_local struct datadog::tracing::TLSStorage*
33+
elastic_apm_profiling_correlation_tls_v1;
2834
#endif
2935

3036
namespace datadog {
@@ -109,6 +115,9 @@ class Tracer {
109115
std::string config() const;
110116

111117
private:
118+
#ifdef __linux__
119+
void correlate(const Span& span);
120+
#endif
112121
void store_config();
113122
};
114123

src/datadog/span.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <datadog/span_config.h>
55
#include <datadog/string_view.h>
66
#include <datadog/trace_segment.h>
7+
#include <datadog/tracer.h>
78

89
#include <cassert>
910
#include <string>
@@ -40,6 +41,16 @@ Span::~Span() {
4041
data_->duration = now - data_->start;
4142
}
4243

44+
#ifdef __linux__
45+
// When a span is finished, we must update the span_id to its parent's.
46+
if (elastic_apm_profiling_correlation_process_storage_v1 != nullptr &&
47+
parent_id().has_value()) {
48+
elastic_apm_profiling_correlation_tls_v1->valid = 0;
49+
elastic_apm_profiling_correlation_tls_v1->span_id = parent_id().value();
50+
elastic_apm_profiling_correlation_tls_v1->valid = 1;
51+
}
52+
#endif
53+
4354
trace_segment_->span_finished();
4455
}
4556

src/datadog/trace_segment.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <datadog/telemetry/metrics.h>
1010
#include <datadog/telemetry/telemetry.h>
1111
#include <datadog/trace_segment.h>
12+
#include <datadog/tracer.h>
1213

1314
#include <cassert>
1415
#include <string>
@@ -137,6 +138,8 @@ Optional<SamplingDecision> TraceSegment::sampling_decision() const {
137138
return sampling_decision_;
138139
}
139140

141+
uint64_t TraceSegment::local_root_id() const { return spans_.front()->span_id; }
142+
140143
Logger& TraceSegment::logger() const { return *logger_; }
141144

142145
void TraceSegment::register_span(std::unique_ptr<SpanData> span) {
@@ -255,6 +258,11 @@ void TraceSegment::span_finished() {
255258
}
256259

257260
telemetry::counter::increment(metrics::tracer::trace_segments_closed);
261+
262+
#ifdef __linux__
263+
// When all spans are finished, so is the current trace.
264+
elastic_apm_profiling_correlation_tls_v1->trace_present = 0;
265+
#endif
258266
}
259267

260268
void TraceSegment::override_sampling_priority(SamplingPriority priority) {

src/datadog/tracer.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535

3636
#ifdef __linux__
3737
const void* elastic_apm_profiling_correlation_process_storage_v1 = nullptr;
38+
thread_local struct datadog::tracing::TLSStorage*
39+
elastic_apm_profiling_correlation_tls_v1 = nullptr;
40+
thread_local std::unique_ptr<datadog::tracing::TLSStorage> tls_info_holder =
41+
nullptr;
3842
#endif
3943

4044
namespace datadog {
@@ -115,6 +119,33 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
115119
store_config();
116120
}
117121

122+
#ifdef __linux__
123+
void Tracer::correlate(const Span& span) {
124+
// See Layout:
125+
// https://github.com/elastic/apm/blob/149cd3e39a77a58002344270ed2ad35357bdd02d/specs/agents/universal-profiling-integration.md#thread-local-storage-layout
126+
tls_info_holder = std::make_unique<datadog::tracing::TLSStorage>();
127+
elastic_apm_profiling_correlation_tls_v1 = tls_info_holder.get();
128+
129+
struct TLSStorage* tls_data = elastic_apm_profiling_correlation_tls_v1;
130+
tls_data->valid = 0;
131+
132+
tls_data->layout_minor_version = 1;
133+
tls_data->trace_present = 1; // We are in a span so no errors
134+
tls_data->trace_flags =
135+
span.trace_segment().sampling_decision().has_value() &&
136+
(span.trace_segment().sampling_decision().value().priority > 0)
137+
? 1
138+
: 0;
139+
auto trace_id = span.trace_id();
140+
tls_data->trace_id_low = trace_id.low;
141+
tls_data->trace_id_high = trace_id.high;
142+
tls_data->span_id = span.id();
143+
tls_data->transaction_id = span.trace_segment().local_root_id();
144+
145+
tls_data->valid = 1;
146+
}
147+
#endif
148+
118149
std::string Tracer::config() const {
119150
// clang-format off
120151
auto config = nlohmann::json::object({
@@ -206,6 +237,11 @@ Span Tracer::create_span(const SpanConfig& config) {
206237
Span span{span_data_ptr, segment,
207238
[generator = generator_]() { return generator->span_id(); },
208239
clock_};
240+
241+
#ifdef __linux__
242+
correlate(span);
243+
#endif
244+
209245
return span;
210246
}
211247

@@ -417,6 +453,11 @@ Expected<Span> Tracer::extract_span(const DictReader& reader,
417453
Span span{span_data_ptr, segment,
418454
[generator = generator_]() { return generator->span_id(); },
419455
clock_};
456+
457+
#ifdef __linux__
458+
correlate(span);
459+
#endif
460+
420461
return span;
421462
}
422463

0 commit comments

Comments
 (0)