Skip to content

Commit 2c670d1

Browse files
committed
feat(profiling): emit SpanExecutionThread JFR event on span finish
1 parent 812c2a4 commit 2c670d1

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilingIntegration.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ public void onSpanFinished(AgentSpan span) {
117117
span.getDurationNano(),
118118
ctx.getEncodedOperationName(),
119119
ctx.getEncodedResourceName());
120+
// Emit the actual execution thread captured in finishAndAddToTrace() so the backend can
121+
// correctly attribute each span to the thread that ran it, rather than the event loop thread
122+
// that calls CoreTracer.write() and commits the SpanNode event above.
123+
long executionThreadId = ctx.getExecutionThreadId();
124+
String executionThreadName = ctx.getExecutionThreadName();
125+
if (executionThreadId > 0 && executionThreadName != null && !executionThreadName.isEmpty()) {
126+
SpanExecutionThreadEvent event = new SpanExecutionThreadEvent();
127+
event.spanId = ctx.getSpanId();
128+
event.executionThreadId = executionThreadId;
129+
event.executionThreadName = executionThreadName;
130+
event.commit();
131+
}
120132
}
121133

122134
@Override
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.datadog.profiling.ddprof;
2+
3+
import jdk.jfr.Category;
4+
import jdk.jfr.Event;
5+
import jdk.jfr.Label;
6+
import jdk.jfr.Name;
7+
import jdk.jfr.StackTrace;
8+
9+
/**
10+
* Pure-Java JFR event that records the actual execution thread for each span. Emitted from {@code
11+
* DatadogProfilingIntegration.onSpanFinished()} using the thread information captured in {@code
12+
* DDSpan.finishAndAddToTrace()} — on the span's own finishing thread, not from the event loop that
13+
* calls {@code CoreTracer.write()}.
14+
*
15+
* <p>The profiling backend ({@code CausalDagExtractor}) reads this event to override the incorrect
16+
* {@code EVENT_THREAD} on {@code datadog.SpanNode} events (which are emitted at trace completion
17+
* from the event loop thread, causing all DAG nodes to appear as event-loop threads).
18+
*/
19+
@Name("datadog.SpanExecutionThread")
20+
@Label("Span Execution Thread")
21+
@Category("Datadog")
22+
@StackTrace(false)
23+
class SpanExecutionThreadEvent extends Event {
24+
25+
@Label("Span ID")
26+
long spanId;
27+
28+
@Label("Execution Thread ID")
29+
long executionThreadId;
30+
31+
@Label("Execution Thread Name")
32+
String executionThreadName;
33+
}

0 commit comments

Comments
 (0)