Skip to content

Commit 33864ab

Browse files
authored
Replace custom TraceInterceptor return values with placeholders to workaround AOT bug in Java 25 (#10272)
1 parent 6fb0d63 commit 33864ab

File tree

2 files changed

+84
-10
lines changed

2 files changed

+84
-10
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package datadog.trace.bootstrap.aot;
2+
3+
import datadog.trace.api.interceptor.MutableSpan;
4+
import datadog.trace.api.interceptor.TraceInterceptor;
5+
import java.util.Collection;
6+
7+
/** Placeholder {@link TraceInterceptor} used to temporarily work around an AOT bug. */
8+
public final class PlaceholderTraceInterceptor implements TraceInterceptor {
9+
public static final PlaceholderTraceInterceptor INSTANCE = new PlaceholderTraceInterceptor();
10+
11+
private PlaceholderTraceInterceptor() {}
12+
13+
@Override
14+
public Collection<? extends MutableSpan> onTraceComplete(
15+
Collection<? extends MutableSpan> trace) {
16+
return trace; // maintain original trace
17+
}
18+
19+
@Override
20+
public int priority() {
21+
return 0;
22+
}
23+
}

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/aot/TraceApiTransformer.java

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
import static datadog.instrument.asm.Opcodes.ACC_ABSTRACT;
44
import static datadog.instrument.asm.Opcodes.ACC_NATIVE;
5+
import static datadog.instrument.asm.Opcodes.ARETURN;
56
import static datadog.instrument.asm.Opcodes.ASM9;
7+
import static datadog.instrument.asm.Opcodes.GETSTATIC;
8+
import static datadog.instrument.asm.Opcodes.ICONST_1;
69
import static datadog.instrument.asm.Opcodes.INVOKEINTERFACE;
10+
import static datadog.instrument.asm.Opcodes.POP;
711
import static datadog.instrument.asm.Opcodes.POP2;
812

913
import datadog.instrument.asm.ClassReader;
@@ -23,11 +27,19 @@
2327
* system class-loader appears to trigger this bug. The workaround is to replace these calls during
2428
* training with opcodes that pop the tracer and argument, and push the expected return value.
2529
*
26-
* <p>Note this transformation is not persisted, so in production the original method is invoked.
30+
* <p>Likewise, custom {@code TraceInterceptor} return values are replaced with simple placeholders
31+
* from the boot class-path which are guaranteed not to trigger the AOT bug.
32+
*
33+
* <p>Note these transformations are not persisted, so in production the original code is used.
2734
*/
2835
final class TraceApiTransformer implements ClassFileTransformer {
2936
private static final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
3037

38+
static final String TRACE_INTERCEPTOR = "datadog/trace/api/interceptor/TraceInterceptor";
39+
40+
static final String PLACEHOLDER_TRACE_INTERCEPTOR =
41+
"datadog/trace/bootstrap/aot/PlaceholderTraceInterceptor";
42+
3143
@Override
3244
public byte[] transform(
3345
ClassLoader loader,
@@ -42,7 +54,7 @@ public byte[] transform(
4254
ClassReader cr = new ClassReader(bytecode);
4355
ClassWriter cw = new ClassWriter(cr, 0);
4456
AtomicBoolean modified = new AtomicBoolean();
45-
cr.accept(new CallerPatch(cw, modified), 0);
57+
cr.accept(new TraceInterceptorPatch(cw, modified), 0);
4658
// only return something when we've modified the bytecode
4759
if (modified.get()) {
4860
return cw.toByteArray();
@@ -54,11 +66,18 @@ public byte[] transform(
5466
return null; // tells the JVM to keep the original bytecode
5567
}
5668

57-
/** Patches callers of {@link datadog.trace.api.Tracer#addTraceInterceptor}. */
58-
static final class CallerPatch extends ClassVisitor {
69+
/**
70+
* Patches certain references to {@code TraceInterceptor} to workaround AOT bug:
71+
*
72+
* <ul>
73+
* <li>removes direct calls to {@code Tracer.addTraceInterceptor()}
74+
* <li>replaces {@code TraceInterceptor} return values with placeholders
75+
* </ul>
76+
*/
77+
static final class TraceInterceptorPatch extends ClassVisitor {
5978
private final AtomicBoolean modified;
6079

61-
CallerPatch(ClassVisitor cv, AtomicBoolean modified) {
80+
TraceInterceptorPatch(ClassVisitor cv, AtomicBoolean modified) {
6281
super(ASM9, cv);
6382
this.modified = modified;
6483
}
@@ -68,14 +87,17 @@ public MethodVisitor visitMethod(
6887
int access, String name, String descriptor, String signature, String[] exceptions) {
6988
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
7089
if ((access & (ACC_ABSTRACT | ACC_NATIVE)) == 0) {
90+
if (descriptor.endsWith(")L" + TRACE_INTERCEPTOR + ";")) {
91+
mv = new ReturnPatch(mv, modified);
92+
}
7193
return new InvokePatch(mv, modified);
7294
} else {
7395
return mv; // no need to patch abstract/native methods
7496
}
7597
}
7698
}
7799

78-
/** Removes calls to {@link datadog.trace.api.Tracer#addTraceInterceptor}. */
100+
/** Removes direct calls to {@code Tracer.addTraceInterceptor()}. */
79101
static final class InvokePatch extends MethodVisitor {
80102
private final AtomicBoolean modified;
81103

@@ -90,16 +112,45 @@ public void visitMethodInsn(
90112
if (INVOKEINTERFACE == opcode
91113
&& "datadog/trace/api/Tracer".equals(owner)
92114
&& "addTraceInterceptor".equals(name)
93-
&& "(Ldatadog/trace/api/interceptor/TraceInterceptor;)Z".equals(descriptor)) {
94-
// pop tracer and trace interceptor argument from call stack
115+
&& ("(L" + TRACE_INTERCEPTOR + ";)Z").equals(descriptor)) {
116+
// discard tracer and trace interceptor argument from call stack
95117
mv.visitInsn(POP2);
96-
// push true return value
97-
mv.visitLdcInsn(true);
118+
// push substitute return value (true)
119+
mv.visitInsn(ICONST_1);
98120
// flag that we've modified the bytecode
99121
modified.set(true);
100122
} else {
101123
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
102124
}
103125
}
104126
}
127+
128+
/** Replaces custom {@code TraceInterceptor} return values with placeholders. */
129+
static final class ReturnPatch extends MethodVisitor {
130+
private final AtomicBoolean modified;
131+
132+
ReturnPatch(MethodVisitor mv, AtomicBoolean modified) {
133+
super(ASM9, mv);
134+
this.modified = modified;
135+
}
136+
137+
@Override
138+
public void visitInsn(int opcode) {
139+
if (ARETURN == opcode) {
140+
// discard old return value from call stack
141+
mv.visitInsn(POP);
142+
// push our placeholder interceptor instead
143+
mv.visitFieldInsn(
144+
GETSTATIC,
145+
PLACEHOLDER_TRACE_INTERCEPTOR,
146+
"INSTANCE",
147+
"L" + PLACEHOLDER_TRACE_INTERCEPTOR + ";");
148+
mv.visitInsn(ARETURN);
149+
// flag that we've modified the bytecode
150+
modified.set(true);
151+
} else {
152+
super.visitInsn(opcode);
153+
}
154+
}
155+
}
105156
}

0 commit comments

Comments
 (0)