Skip to content

Commit 35ba4e5

Browse files
committed
Keep the correct profiling context on vthread mount/unmount
1 parent 4655032 commit 35ba4e5

File tree

3 files changed

+470
-0
lines changed

3 files changed

+470
-0
lines changed

dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/ignored_class_name.trie

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
1 java.util.logging.LogManager$Cleaner
6969
# Need for IAST
7070
0 java.io.PushbackInputStream
71+
0 java.lang.VirtualThread
7172
1 jdk.*
7273
0 jdk.internal.net.http.*
7374
1 net.bytebuddy.*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package datadog.trace.instrumentation.java.concurrent.virtualthread;
2+
3+
import static java.util.Collections.singletonMap;
4+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
5+
import static net.bytebuddy.matcher.ElementMatchers.named;
6+
7+
import com.google.auto.service.AutoService;
8+
import datadog.environment.JavaVirtualMachine;
9+
import datadog.trace.agent.tooling.Instrumenter;
10+
import datadog.trace.agent.tooling.InstrumenterModule;
11+
import datadog.trace.api.Stateful;
12+
import datadog.trace.bootstrap.ContextStore;
13+
import datadog.trace.bootstrap.InstrumentationContext;
14+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
15+
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
16+
import datadog.trace.bootstrap.instrumentation.api.ProfilerContext;
17+
import java.util.Map;
18+
import net.bytebuddy.asm.Advice;
19+
20+
/**
21+
* Instruments the VirtualThread class to enable profiler context propagation on vthread
22+
* mount/unmount.
23+
*
24+
* <ul>
25+
* <li>https://github.com/openjdk/jdk/blob/6a4c2676a6378f573bd58d1bc32b57765d756291/src/java.base/share/classes/java/lang/VirtualThread.java#L478
26+
* <li>https://github.com/openjdk/jdk/blob/6a4c2676a6378f573bd58d1bc32b57765d756291/src/java.base/share/classes/java/lang/VirtualThread.java#L508
27+
* </ul>
28+
*/
29+
@AutoService(InstrumenterModule.class)
30+
public class ThreadContinuationInstrumentation extends InstrumenterModule.Profiling
31+
implements Instrumenter.ForBootstrap, Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
32+
public ThreadContinuationInstrumentation() {
33+
super("java_concurrent", "vthread-continuation");
34+
}
35+
36+
@Override
37+
public String instrumentedType() {
38+
return "java.lang.VirtualThread";
39+
}
40+
41+
@Override
42+
public boolean isEnabled() {
43+
return JavaVirtualMachine.isJavaVersionAtLeast(19) && super.isEnabled();
44+
}
45+
46+
@Override
47+
public Map<String, String> contextStore() {
48+
return singletonMap(Thread.class.getName(), Stateful.class.getName());
49+
}
50+
51+
@Override
52+
public void methodAdvice(MethodTransformer transformer) {
53+
transformer.applyAdvice(isMethod().and(named("mount")), getClass().getName() + "$Mount");
54+
transformer.applyAdvice(isMethod().and(named("unmount")), getClass().getName() + "$Unmount");
55+
}
56+
57+
public static final class Mount {
58+
@Advice.OnMethodExit(suppress = Throwable.class)
59+
public static void onMount(@Advice.FieldValue("carrierThread") Thread carrier) {
60+
ContextStore<Thread, Stateful> contextStore =
61+
InstrumentationContext.get(Thread.class, Stateful.class);
62+
// The TLS has been rerouted after 'mount' - we can get the vthread relative active span
63+
AgentSpan span = AgentTracer.activeSpan();
64+
// we are fully mounted and carrier points to the correct OS thread
65+
Stateful stateful = contextStore.get(carrier);
66+
ProfilerContext ctx = (ProfilerContext) span.context();
67+
if (stateful == null) {
68+
stateful = AgentTracer.get().getProfilingContext().newScopeState(ctx);
69+
// associate the stateful with the carrier thread
70+
contextStore.put(carrier, stateful);
71+
} else {
72+
// just make sure we really close all statefuls
73+
stateful.close();
74+
}
75+
// activate the stateful with the proper context and propagate the context to profiler
76+
stateful.activate(ctx);
77+
}
78+
}
79+
80+
public static final class Unmount {
81+
@Advice.OnMethodEnter(suppress = Throwable.class)
82+
public static void onUnmount(@Advice.FieldValue("carrierThread") Thread carrier) {
83+
ContextStore<Thread, Stateful> contextStore =
84+
InstrumentationContext.get(Thread.class, Stateful.class);
85+
// The carrier thread still points to the carrying thread; we have not unmounted yet
86+
Stateful stateful = contextStore.get(carrier);
87+
if (stateful != null) {
88+
// close the stateful, clean up the profiling context
89+
stateful.close();
90+
}
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)