Skip to content

Commit c48213e

Browse files
committed
Enable instrumentation of synthetic methods
1 parent 0a38143 commit c48213e

4 files changed

Lines changed: 123 additions & 4 deletions

File tree

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.nameStartsWith;
66
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
77
import static net.bytebuddy.matcher.ElementMatchers.any;
8+
import static net.bytebuddy.matcher.ElementMatchers.isDefaultFinalizer;
89
import static net.bytebuddy.matcher.ElementMatchers.none;
910

1011
import datadog.trace.agent.tooling.context.FieldBackedContextProvider;
@@ -20,12 +21,14 @@
2021
import java.util.ServiceLoader;
2122
import java.util.Set;
2223
import java.util.concurrent.CopyOnWriteArrayList;
24+
import net.bytebuddy.ByteBuddy;
2325
import net.bytebuddy.agent.builder.AgentBuilder;
2426
import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
2527
import net.bytebuddy.description.type.TypeDefinition;
2628
import net.bytebuddy.description.type.TypeDescription;
2729
import net.bytebuddy.dynamic.DynamicType;
2830
import net.bytebuddy.matcher.ElementMatcher;
31+
import net.bytebuddy.matcher.LatentMatcher;
2932
import net.bytebuddy.utility.JavaModule;
3033
import org.slf4j.Logger;
3134
import org.slf4j.LoggerFactory;
@@ -80,8 +83,12 @@ public static ResettableClassFileTransformer installBytebuddyAgent(
8083

8184
FieldBackedContextProvider.resetContextMatchers();
8285

86+
// By default ByteBuddy will skip all methods that are synthetic or default finalizer
87+
// but we need to instrument some synthetic methods in Scala, so change the ignore matcher
88+
ByteBuddy byteBuddy =
89+
new ByteBuddy().ignore(new LatentMatcher.Resolved<>(isDefaultFinalizer()));
8390
AgentBuilder.Ignored ignoredAgentBuilder =
84-
new AgentBuilder.Default()
91+
new AgentBuilder.Default(byteBuddy)
8592
.disableClassFormatChanges()
8693
.assureReadEdgeTo(INSTRUMENTATION, FieldBackedContextAccessor.class)
8794
.with(AgentTooling.transformerDecorator())

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static java.util.Collections.singletonMap;
88
import static net.bytebuddy.matcher.ElementMatchers.any;
99
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
10+
import static net.bytebuddy.matcher.ElementMatchers.isSynthetic;
1011
import static net.bytebuddy.matcher.ElementMatchers.not;
1112

1213
import datadog.trace.agent.tooling.bytebuddy.ExceptionHandlers;
@@ -160,7 +161,7 @@ public final AgentBuilder instrument(final AgentBuilder parentAgentBuilder) {
160161
if (transformer != null) {
161162
agentBuilder = agentBuilder.transform(transformer);
162163
}
163-
AdviceBuilder adviceBuilder = new AdviceBuilder(agentBuilder);
164+
AdviceBuilder adviceBuilder = new AdviceBuilder(agentBuilder, methodIgnoreMatcher());
164165
adviceTransformations(adviceBuilder);
165166
agentBuilder = adviceBuilder.agentBuilder;
166167
agentBuilder = contextProvider.additionalInstrumentation(agentBuilder);
@@ -202,9 +203,13 @@ private AgentBuilder.Identified.Extendable injectHelperClasses(
202203

203204
private static class AdviceBuilder implements AdviceTransformation {
204205
AgentBuilder.Identified.Extendable agentBuilder;
206+
final ElementMatcher<? super MethodDescription> ignoreMatcher;
205207

206-
public AdviceBuilder(AgentBuilder.Identified.Extendable agentBuilder) {
208+
public AdviceBuilder(
209+
AgentBuilder.Identified.Extendable agentBuilder,
210+
ElementMatcher<? super MethodDescription> ignoreMatcher) {
207211
this.agentBuilder = agentBuilder;
212+
this.ignoreMatcher = ignoreMatcher;
208213
}
209214

210215
@Override
@@ -214,7 +219,7 @@ public void applyAdvice(ElementMatcher<? super MethodDescription> matcher, Strin
214219
new AgentBuilder.Transformer.ForAdvice()
215220
.include(Utils.getBootstrapProxy(), Utils.getAgentClassLoader())
216221
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler())
217-
.advice(matcher, name));
222+
.advice(not(ignoreMatcher).and(matcher), name));
218223
}
219224
}
220225

@@ -299,6 +304,14 @@ public AgentBuilder.Transformer transformer() {
299304
return null;
300305
}
301306

307+
/** @return A type matcher used to ignore some methods when applying transformation. */
308+
public ElementMatcher<? super MethodDescription> methodIgnoreMatcher() {
309+
// By default ByteBuddy will skip all methods that are synthetic at the top level, but since
310+
// we need to instrument some synthetic methods in Scala and changed that, we make the default
311+
// here to ignore synthetic methods to not change the behavior for unaware instrumentations
312+
return isSynthetic();
313+
}
314+
302315
/**
303316
* Instrumenters should register each advice transformation by calling {@link
304317
* AdviceTransformation#applyAdvice(ElementMatcher, String)} one or more times.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package synthetic
2+
3+
import datadog.trace.agent.test.AgentTestRunner
4+
5+
abstract class SyntheticForkedTestBase extends AgentTestRunner {
6+
7+
def "test Synthetic methods"() {
8+
expect:
9+
SyntheticTestInstrumentation.Compute.result(i) == computed(i)
10+
11+
where:
12+
i << [0, 1, 2, 3]
13+
}
14+
15+
abstract int computed(int i)
16+
}
17+
18+
class SyntheticForkedTest extends SyntheticForkedTestBase {
19+
20+
@Override
21+
int computed(int i) {
22+
return i * 2 + 1
23+
}
24+
25+
@Override
26+
protected void configurePreAgent() {
27+
super.configurePreAgent()
28+
29+
System.setProperty("synthetic.test.enabled", "true")
30+
}
31+
}
32+
33+
class SyntheticDisabledForkedTest extends SyntheticForkedTestBase {
34+
35+
@Override
36+
int computed(int i) {
37+
return i + 1
38+
}
39+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package synthetic;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4+
import static net.bytebuddy.matcher.ElementMatchers.none;
5+
6+
import com.google.auto.service.AutoService;
7+
import datadog.trace.agent.tooling.Instrumenter;
8+
import net.bytebuddy.asm.Advice;
9+
import net.bytebuddy.description.method.MethodDescription;
10+
import net.bytebuddy.description.type.TypeDescription;
11+
import net.bytebuddy.matcher.ElementMatcher;
12+
13+
@AutoService(Instrumenter.class)
14+
public class SyntheticTestInstrumentation extends Instrumenter.Tracing {
15+
16+
public SyntheticTestInstrumentation() {
17+
super("synthetic-test");
18+
}
19+
20+
@Override
21+
public ElementMatcher<? super TypeDescription> typeMatcher() {
22+
return named(getClass().getName() + "$WithSynthetic");
23+
}
24+
25+
@Override
26+
public ElementMatcher<? super MethodDescription> methodIgnoreMatcher() {
27+
if (Boolean.getBoolean("synthetic.test.enabled")) {
28+
return none();
29+
} else {
30+
return super.methodIgnoreMatcher();
31+
}
32+
}
33+
34+
@Override
35+
public void adviceTransformations(AdviceTransformation transformation) {
36+
transformation.applyAdvice(named("access$000"), getClass().getName() + "$AccessAdvice");
37+
}
38+
39+
public static class Compute {
40+
public static Integer result(Integer i) {
41+
WithSynthetic p = new WithSynthetic(i);
42+
return p.secret + 1;
43+
}
44+
}
45+
46+
private static final class WithSynthetic {
47+
private Integer secret;
48+
49+
public WithSynthetic(Integer secret) {
50+
this.secret = secret;
51+
}
52+
}
53+
54+
public static final class AccessAdvice {
55+
@Advice.OnMethodExit(suppress = Throwable.class)
56+
public static void exit(@Advice.Return(readOnly = false) Integer i) {
57+
i = i * 2;
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)