Skip to content

Commit ef6e3dc

Browse files
committed
Install AsyncResult extensions during class initialization
1 parent 39b32cf commit ef6e3dc

File tree

9 files changed

+124
-54
lines changed

9 files changed

+124
-54
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package datadog.trace.agent.tooling;
2+
3+
import datadog.trace.util.Strings;
4+
import net.bytebuddy.asm.AsmVisitorWrapper;
5+
import net.bytebuddy.description.field.FieldDescription;
6+
import net.bytebuddy.description.field.FieldList;
7+
import net.bytebuddy.description.method.MethodList;
8+
import net.bytebuddy.description.type.TypeDescription;
9+
import net.bytebuddy.implementation.Implementation;
10+
import net.bytebuddy.jar.asm.ClassVisitor;
11+
import net.bytebuddy.jar.asm.MethodVisitor;
12+
import net.bytebuddy.jar.asm.Opcodes;
13+
import net.bytebuddy.pool.TypePool;
14+
15+
public final class PreloadingVisitor implements AsmVisitorWrapper {
16+
private final String internalPreloadName;
17+
18+
public PreloadingVisitor(String preloadClass) {
19+
this.internalPreloadName = Strings.getInternalName(preloadClass);
20+
}
21+
22+
@Override
23+
public int mergeWriter(int flags) {
24+
return flags;
25+
}
26+
27+
@Override
28+
public int mergeReader(int flags) {
29+
return flags;
30+
}
31+
32+
@Override
33+
public ClassVisitor wrap(
34+
final TypeDescription instrumentedType,
35+
final ClassVisitor classVisitor,
36+
final Implementation.Context implementationContext,
37+
final TypePool typePool,
38+
final FieldList<FieldDescription.InDefinedShape> fields,
39+
final MethodList<?> methods,
40+
final int writerFlags,
41+
final int readerFlags) {
42+
return new ClassVisitor(Opcodes.ASM8, classVisitor) {
43+
44+
private static final String TYPE_INITIALIZER_NAME = "<clinit>";
45+
private static final String VOID_METHOD_DESCRIPTOR = "()V";
46+
47+
private boolean hasTypeInitializer;
48+
49+
@Override
50+
public MethodVisitor visitMethod(
51+
int access, String name, String descriptor, String signature, String[] exceptions) {
52+
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
53+
if (TYPE_INITIALIZER_NAME.equals(name)) {
54+
hasTypeInitializer = true;
55+
return new MethodVisitor(Opcodes.ASM8, mv) {
56+
@Override
57+
public void visitCode() {
58+
visitMethodInsn(
59+
Opcodes.INVOKESTATIC, internalPreloadName, "init", VOID_METHOD_DESCRIPTOR, false);
60+
super.visitCode();
61+
}
62+
};
63+
}
64+
return mv;
65+
}
66+
67+
@Override
68+
public void visitEnd() {
69+
if (!hasTypeInitializer) {
70+
MethodVisitor mv =
71+
visitMethod(
72+
Opcodes.ACC_STATIC, TYPE_INITIALIZER_NAME, VOID_METHOD_DESCRIPTOR, null, null);
73+
mv.visitCode();
74+
mv.visitMethodInsn(
75+
Opcodes.INVOKESTATIC, internalPreloadName, "init", VOID_METHOD_DESCRIPTOR, false);
76+
mv.visitInsn(Opcodes.RETURN);
77+
mv.visitMaxs(0, 0);
78+
mv.visitEnd();
79+
}
80+
super.visitEnd();
81+
}
82+
};
83+
}
84+
}

dd-java-agent/instrumentation/guava-10/src/main/java/datadog/trace/instrumentation/guava10/GuavaAsyncResultExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class GuavaAsyncResultExtension implements AsyncResultExtension {
1919
* class initialization. This will ensure this extension will only be registered once under {@link
2020
* AsyncResultExtensions}.
2121
*/
22-
public static void initialize() {}
22+
public static void init() {}
2323

2424
@Override
2525
public boolean supports(Class<?> result) {

dd-java-agent/instrumentation/guava-10/src/main/java/datadog/trace/instrumentation/guava10/ListenableFutureInstrumentation.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
44
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
55
import static java.util.Collections.singletonMap;
6-
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
76
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
87

98
import com.google.auto.service.AutoService;
109
import com.google.common.util.concurrent.AbstractFuture;
1110
import datadog.trace.agent.tooling.Instrumenter;
1211
import datadog.trace.agent.tooling.InstrumenterModule;
12+
import datadog.trace.agent.tooling.PreloadingVisitor;
1313
import datadog.trace.bootstrap.ContextStore;
1414
import datadog.trace.bootstrap.InstrumentationContext;
1515
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
@@ -22,7 +22,7 @@
2222

2323
@AutoService(InstrumenterModule.class)
2424
public class ListenableFutureInstrumentation extends InstrumenterModule.Tracing
25-
implements Instrumenter.ForSingleType {
25+
implements Instrumenter.ForSingleType, Instrumenter.HasTypeAdvice {
2626

2727
public ListenableFutureInstrumentation() {
2828
super("guava");
@@ -45,22 +45,18 @@ public Map<String, String> contextStore() {
4545
return singletonMap(Runnable.class.getName(), State.class.getName());
4646
}
4747

48+
@Override
49+
public void typeAdvice(TypeTransformer transformer) {
50+
transformer.applyAdvice(new PreloadingVisitor(packageName + ".GuavaAsyncResultExtension"));
51+
}
52+
4853
@Override
4954
public void methodAdvice(MethodTransformer transformer) {
50-
transformer.applyAdvice(
51-
isConstructor(), ListenableFutureInstrumentation.class.getName() + "$AbstractFutureAdvice");
5255
transformer.applyAdvice(
5356
named("addListener").and(takesArguments(Runnable.class, Executor.class)),
5457
ListenableFutureInstrumentation.class.getName() + "$AddListenerAdvice");
5558
}
5659

57-
public static class AbstractFutureAdvice {
58-
@Advice.OnMethodExit(suppress = Throwable.class)
59-
public static void init() {
60-
GuavaAsyncResultExtension.initialize();
61-
}
62-
}
63-
6460
public static class AddListenerAdvice {
6561
@Advice.OnMethodEnter(suppress = Throwable.class)
6662
public static State addListenerEnter(

dd-java-agent/instrumentation/reactive-streams/src/main/java/datadog/trace/instrumentation/reactivestreams/PublisherInstrumentation.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
66
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
77
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
8-
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
98
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
109
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
1110
import static net.bytebuddy.matcher.ElementMatchers.not;
@@ -15,6 +14,7 @@
1514
import com.google.auto.service.AutoService;
1615
import datadog.trace.agent.tooling.Instrumenter;
1716
import datadog.trace.agent.tooling.InstrumenterModule;
17+
import datadog.trace.agent.tooling.PreloadingVisitor;
1818
import datadog.trace.bootstrap.InstrumentationContext;
1919
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
2020
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
@@ -33,7 +33,7 @@
3333
*/
3434
@AutoService(InstrumenterModule.class)
3535
public class PublisherInstrumentation extends InstrumenterModule.Tracing
36-
implements Instrumenter.ForTypeHierarchy {
36+
implements Instrumenter.ForTypeHierarchy, Instrumenter.HasTypeAdvice {
3737

3838
public PublisherInstrumentation() {
3939
super("reactive-streams", "reactive-streams-1");
@@ -67,9 +67,14 @@ public String[] helperClassNames() {
6767
};
6868
}
6969

70+
@Override
71+
public void typeAdvice(TypeTransformer transformer) {
72+
transformer.applyAdvice(
73+
new PreloadingVisitor(packageName + ".ReactiveStreamsAsyncResultExtension"));
74+
}
75+
7076
@Override
7177
public void methodAdvice(MethodTransformer transformer) {
72-
transformer.applyAdvice(isConstructor(), this.getClass().getName() + "$PublisherAdvice");
7378
transformer.applyAdvice(
7479
isMethod()
7580
.and(not(isStatic()))
@@ -79,13 +84,6 @@ public void methodAdvice(MethodTransformer transformer) {
7984
getClass().getName() + "$PublisherSubscribeAdvice");
8085
}
8186

82-
public static class PublisherAdvice {
83-
@Advice.OnMethodExit(suppress = Throwable.class)
84-
public static void init() {
85-
ReactiveStreamsAsyncResultExtension.initialize();
86-
}
87-
}
88-
8987
public static class PublisherSubscribeAdvice {
9088
@Advice.OnMethodEnter(suppress = Throwable.class)
9189
public static AgentScope onSubscribe(

dd-java-agent/instrumentation/reactive-streams/src/main/java/datadog/trace/instrumentation/reactivestreams/ReactiveStreamsAsyncResultExtension.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ public class ReactiveStreamsAsyncResultExtension implements AsyncResultExtension
1919
* class initialization. This will ensure this extension will only be registered once under {@link
2020
* AsyncResultExtensions}.
2121
*/
22-
public static void initialize() {}
22+
public static void init() {}
2323

2424
@Override
2525
public boolean supports(Class<?> result) {
26-
boolean ret = result == Publisher.class;
27-
return ret;
26+
return result == Publisher.class;
2827
}
2928

3029
@Override

dd-java-agent/instrumentation/reactor-core-3.1/src/main/java/datadog/trace/instrumentation/reactor/core/BlockingPublisherInstrumentation.java

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith;
55
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
66
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
7-
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
87
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
98

109
import com.google.auto.service.AutoService;
1110
import datadog.trace.agent.tooling.Instrumenter;
1211
import datadog.trace.agent.tooling.InstrumenterModule;
12+
import datadog.trace.agent.tooling.PreloadingVisitor;
1313
import datadog.trace.bootstrap.InstrumentationContext;
1414
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
1515
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
@@ -28,7 +28,7 @@
2828
*/
2929
@AutoService(InstrumenterModule.class)
3030
public class BlockingPublisherInstrumentation extends InstrumenterModule.Tracing
31-
implements Instrumenter.ForTypeHierarchy {
31+
implements Instrumenter.ForTypeHierarchy, Instrumenter.HasTypeAdvice {
3232
public BlockingPublisherInstrumentation() {
3333
super("reactor-core");
3434
}
@@ -40,13 +40,6 @@ public String[] helperClassNames() {
4040
};
4141
}
4242

43-
@Override
44-
public void methodAdvice(MethodTransformer transformer) {
45-
transformer.applyAdvice(isConstructor(), getClass().getName() + "$AsyncExtensionInstallAdvice");
46-
transformer.applyAdvice(
47-
isMethod().and(nameStartsWith("block")), getClass().getName() + "$BlockingAdvice");
48-
}
49-
5043
@Override
5144
public Map<String, String> contextStore() {
5245
return Collections.singletonMap("org.reactivestreams.Publisher", AgentSpan.class.getName());
@@ -62,6 +55,17 @@ public ElementMatcher<TypeDescription> hierarchyMatcher() {
6255
return hasSuperType(namedOneOf("reactor.core.publisher.Mono", "reactor.core.publisher.Flux"));
6356
}
6457

58+
@Override
59+
public void typeAdvice(TypeTransformer transformer) {
60+
transformer.applyAdvice(new PreloadingVisitor(packageName + ".ReactorAsyncResultExtension"));
61+
}
62+
63+
@Override
64+
public void methodAdvice(MethodTransformer transformer) {
65+
transformer.applyAdvice(
66+
isMethod().and(nameStartsWith("block")), getClass().getName() + "$BlockingAdvice");
67+
}
68+
6569
public static class BlockingAdvice {
6670
@Advice.OnMethodEnter(suppress = Throwable.class)
6771
public static AgentScope before(@Advice.This final Publisher self) {
@@ -79,11 +83,4 @@ public static void after(@Advice.Enter final AgentScope scope) {
7983
}
8084
}
8185
}
82-
83-
public static class AsyncExtensionInstallAdvice {
84-
@Advice.OnMethodExit(suppress = Throwable.class)
85-
public static void init() {
86-
ReactorAsyncResultExtension.initialize();
87-
}
88-
}
8986
}

dd-java-agent/instrumentation/reactor-core-3.1/src/main/java/datadog/trace/instrumentation/reactor/core/ReactorAsyncResultExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class ReactorAsyncResultExtension implements AsyncResultExtension {
1818
* class initialization. This will ensure this extension will only be registered once under {@link
1919
* AsyncResultExtensions}.
2020
*/
21-
public static void initialize() {}
21+
public static void init() {}
2222

2323
@Override
2424
public boolean supports(Class<?> result) {

dd-java-agent/instrumentation/rxjava-2/src/main/java/datadog/trace/instrumentation/rxjava2/RxJavaAsyncResultExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class RxJavaAsyncResultExtension implements AsyncResultExtension {
2121
* class initialization. This will ensure this extension will only be registered once under {@link
2222
* AsyncResultExtensions}.
2323
*/
24-
public static void initialize() {}
24+
public static void init() {}
2525

2626
@Override
2727
public boolean supports(Class<?> result) {

dd-java-agent/instrumentation/rxjava-2/src/main/java/datadog/trace/instrumentation/rxjava2/RxJavaPluginsInstrumentation.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
package datadog.trace.instrumentation.rxjava2;
22

3-
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
4-
53
import com.google.auto.service.AutoService;
64
import datadog.trace.agent.tooling.Instrumenter;
75
import datadog.trace.agent.tooling.InstrumenterModule;
6+
import datadog.trace.agent.tooling.PreloadingVisitor;
87
import datadog.trace.api.InstrumenterConfig;
9-
import net.bytebuddy.asm.Advice;
108

119
@AutoService(InstrumenterModule.class)
1210
public class RxJavaPluginsInstrumentation extends InstrumenterModule.Tracing
13-
implements Instrumenter.ForSingleType {
11+
implements Instrumenter.ForSingleType, Instrumenter.HasTypeAdvice {
1412

1513
public RxJavaPluginsInstrumentation() {
1614
super("rxjava");
@@ -35,14 +33,12 @@ public String[] helperClassNames() {
3533
}
3634

3735
@Override
38-
public void methodAdvice(MethodTransformer transformer) {
39-
transformer.applyAdvice(isMethod(), getClass().getName() + "$RxJavaPluginsAdvice");
36+
public void typeAdvice(TypeTransformer transformer) {
37+
transformer.applyAdvice(new PreloadingVisitor(packageName + ".RxJavaAsyncResultExtension"));
4038
}
4139

42-
public static class RxJavaPluginsAdvice {
43-
@Advice.OnMethodExit(suppress = Throwable.class)
44-
public static void init() {
45-
RxJavaAsyncResultExtension.initialize();
46-
}
40+
@Override
41+
public void methodAdvice(MethodTransformer transformer) {
42+
// no-op
4743
}
4844
}

0 commit comments

Comments
 (0)