Skip to content

Commit 5ca2bf6

Browse files
committed
Re-use AWS SDK module to instrument EMR's shaded copy
1 parent a8b33d5 commit 5ca2bf6

File tree

13 files changed

+200
-23
lines changed

13 files changed

+200
-23
lines changed

dd-java-agent/agent-builder/src/main/java/datadog/trace/agent/tooling/CombiningTransformerBuilder.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public final class CombiningTransformerBuilder
7373
private AgentBuilder.Transformer contextRequestRewriter;
7474
private HelperTransformer helperTransformer;
7575
private Advice.PostProcessor.Factory postProcessor;
76+
private AdviceShader adviceShader;
7677
private MuzzleCheck muzzle;
7778

7879
// temporary buffer for collecting advice; reset for each instrumenter
@@ -125,10 +126,14 @@ private void prepareInstrumentation(InstrumenterModule module, int instrumentati
125126
helperTransformer =
126127
helperClassNames.length > 0
127128
? new HelperTransformer(
128-
module.useAgentCodeSource(), module.getClass().getSimpleName(), helperClassNames)
129+
module.useAgentCodeSource(),
130+
adviceShader,
131+
module.getClass().getSimpleName(),
132+
helperClassNames)
129133
: null;
130134

131135
postProcessor = module.postProcessor();
136+
adviceShader = AdviceShader.with(module.adviceShading());
132137

133138
muzzle = new MuzzleCheck(module, instrumentationId);
134139
}
@@ -238,11 +243,14 @@ public void applyAdvice(ElementMatcher<? super MethodDescription> matcher, Strin
238243
if (postProcessor != null) {
239244
customMapping = customMapping.with(postProcessor);
240245
}
241-
advice.add(
246+
AgentBuilder.Transformer.ForAdvice forAdvice =
242247
new AgentBuilder.Transformer.ForAdvice(customMapping)
243248
.include(Utils.getBootstrapProxy(), Utils.getExtendedClassLoader())
244-
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler())
245-
.advice(not(ignoredMethods).and(matcher), adviceClass));
249+
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler());
250+
if (adviceShader != null) {
251+
forAdvice = forAdvice.with(ShadedClassFiles.strategy(adviceShader));
252+
}
253+
advice.add(forAdvice.advice(not(ignoredMethods).and(matcher), adviceClass));
246254
}
247255

248256
public ClassFileTransformer installOn(Instrumentation instrumentation) {
@@ -342,8 +350,11 @@ public DynamicType.Builder<?> transform(
342350

343351
static final class HelperTransformer extends HelperInjector implements AgentBuilder.Transformer {
344352
HelperTransformer(
345-
boolean useAgentCodeSource, String requestingName, String... helperClassNames) {
346-
super(useAgentCodeSource, requestingName, helperClassNames);
353+
boolean useAgentCodeSource,
354+
AdviceShader adviceShader,
355+
String requestingName,
356+
String... helperClassNames) {
357+
super(useAgentCodeSource, adviceShader, requestingName, helperClassNames);
347358
}
348359
}
349360

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package datadog.trace.agent.tooling;
2+
3+
import static net.bytebuddy.agent.builder.AgentBuilder.LocationStrategy.ForClassLoader.STRONG;
4+
5+
import java.io.IOException;
6+
import net.bytebuddy.agent.builder.AgentBuilder;
7+
import net.bytebuddy.dynamic.ClassFileLocator;
8+
9+
/** Locates and shades class-file resources. */
10+
public final class ShadedClassFiles implements ClassFileLocator {
11+
private final ClassFileLocator delegate;
12+
private final AdviceShader adviceShader;
13+
14+
/** Strategy that uses the given shader to shade any located class-file resources. */
15+
public static AgentBuilder.LocationStrategy strategy(AdviceShader adviceShader) {
16+
return (classLoader, javaModule) ->
17+
new ShadedClassFiles(STRONG.classFileLocator(classLoader, javaModule), adviceShader);
18+
}
19+
20+
ShadedClassFiles(ClassFileLocator delegate, AdviceShader adviceShader) {
21+
this.delegate = delegate;
22+
this.adviceShader = adviceShader;
23+
}
24+
25+
@Override
26+
public Resolution locate(String className) throws IOException {
27+
final Resolution resolution = delegate.locate(className);
28+
if (resolution.isResolved()) {
29+
return new Resolution.Explicit(adviceShader.shade(resolution.resolve()));
30+
} else {
31+
return resolution;
32+
}
33+
}
34+
35+
@Override
36+
public void close() throws IOException {
37+
delegate.close();
38+
}
39+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package datadog.trace.agent.tooling;
2+
3+
import datadog.trace.api.cache.DDCache;
4+
import datadog.trace.api.cache.DDCaches;
5+
import java.util.function.Function;
6+
import net.bytebuddy.jar.asm.ClassReader;
7+
import net.bytebuddy.jar.asm.ClassVisitor;
8+
import net.bytebuddy.jar.asm.ClassWriter;
9+
import net.bytebuddy.jar.asm.commons.ClassRemapper;
10+
import net.bytebuddy.jar.asm.commons.Remapper;
11+
12+
/** Shades advice bytecode by applying a shading function to all references. */
13+
public final class AdviceShader extends Remapper {
14+
private final DDCache<String, String> cache = DDCaches.newFixedSizeCache(64);
15+
private final Function<String, String> shading;
16+
17+
public static AdviceShader with(Function<String, String> shading) {
18+
return shading != null ? new AdviceShader(shading) : null;
19+
}
20+
21+
AdviceShader(Function<String, String> shading) {
22+
this.shading = shading;
23+
}
24+
25+
/** Applies shading before calling the given {@link ClassVisitor}. */
26+
public ClassVisitor shade(ClassVisitor cv) {
27+
return new ClassRemapper(cv, this);
28+
}
29+
30+
/** Returns the result of shading the given bytecode. */
31+
public byte[] shade(byte[] bytecode) {
32+
ClassReader cr = new ClassReader(bytecode);
33+
ClassWriter cw = new ClassWriter(cr, 0);
34+
cr.accept(shade(cw), 0);
35+
return cw.toByteArray();
36+
}
37+
38+
@Override
39+
public String map(String name) {
40+
return cache.computeIfAbsent(name, shading);
41+
}
42+
}

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class HelperInjector implements Instrumenter.TransformingAdvice {
3333
ClassFileLocator.ForClassLoader.of(Utils.getExtendedClassLoader());
3434

3535
private final boolean useAgentCodeSource;
36+
private final AdviceShader adviceShader;
3637
private final String requestingName;
3738

3839
private final Set<String> helperClassNames;
@@ -58,8 +59,17 @@ public HelperInjector(
5859
final boolean useAgentCodeSource,
5960
final String requestingName,
6061
final String... helperClassNames) {
62+
this(useAgentCodeSource, null, requestingName, helperClassNames);
63+
}
64+
65+
public HelperInjector(
66+
final boolean useAgentCodeSource,
67+
final AdviceShader adviceShader,
68+
final String requestingName,
69+
final String... helperClassNames) {
6170
this.useAgentCodeSource = useAgentCodeSource;
6271
this.requestingName = requestingName;
72+
this.adviceShader = adviceShader;
6373

6474
this.helperClassNames = new LinkedHashSet<>(Arrays.asList(helperClassNames));
6575
}
@@ -70,6 +80,7 @@ public HelperInjector(
7080
final Map<String, byte[]> helperMap) {
7181
this.useAgentCodeSource = useAgentCodeSource;
7282
this.requestingName = requestingName;
83+
this.adviceShader = null;
7384

7485
helperClassNames = helperMap.keySet();
7586
dynamicTypeMap.putAll(helperMap);
@@ -78,9 +89,11 @@ public HelperInjector(
7889
private Map<String, byte[]> getHelperMap() throws IOException {
7990
if (dynamicTypeMap.isEmpty()) {
8091
final Map<String, byte[]> classnameToBytes = new LinkedHashMap<>();
81-
8292
for (final String helperClassName : helperClassNames) {
83-
final byte[] classBytes = classFileLocator.locate(helperClassName).resolve();
93+
byte[] classBytes = classFileLocator.locate(helperClassName).resolve();
94+
if (adviceShader != null) {
95+
classBytes = adviceShader.shade(classBytes);
96+
}
8497
classnameToBytes.put(helperClassName, classBytes);
8598
}
8699

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222
import java.util.Map;
2323
import java.util.Set;
24+
import java.util.function.Function;
2425
import net.bytebuddy.asm.Advice;
2526
import net.bytebuddy.description.method.MethodDescription;
2627
import net.bytebuddy.matcher.ElementMatcher;
@@ -161,6 +162,11 @@ public Advice.PostProcessor.Factory postProcessor() {
161162
return null;
162163
}
163164

165+
/** Override this to apply shading to method advice and injected helpers. */
166+
public Function<String, String> adviceShading() {
167+
return null;
168+
}
169+
164170
/**
165171
* Context stores to define for this instrumentation. Are added to matching class loaders.
166172
*

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleGenerator.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.agent.tooling.muzzle;
22

3+
import datadog.trace.agent.tooling.AdviceShader;
34
import datadog.trace.agent.tooling.Instrumenter;
45
import datadog.trace.agent.tooling.InstrumenterModule;
56
import java.io.File;
@@ -78,7 +79,8 @@ public ClassVisitor wrap(
7879
return classVisitor;
7980
}
8081

81-
private static Reference[] generateReferences(Instrumenter.HasMethodAdvice instrumenter) {
82+
private static Reference[] generateReferences(
83+
Instrumenter.HasMethodAdvice instrumenter, AdviceShader adviceShader) {
8284
// track sources we've generated references from to avoid recursion
8385
final Set<String> referenceSources = new HashSet<>();
8486
final Map<String, Reference> references = new LinkedHashMap<>();
@@ -88,7 +90,8 @@ private static Reference[] generateReferences(Instrumenter.HasMethodAdvice instr
8890
for (String adviceClass : adviceClasses) {
8991
if (referenceSources.add(adviceClass)) {
9092
for (Map.Entry<String, Reference> entry :
91-
ReferenceCreator.createReferencesFrom(adviceClass, contextClassLoader).entrySet()) {
93+
ReferenceCreator.createReferencesFrom(adviceClass, adviceShader, contextClassLoader)
94+
.entrySet()) {
9295
Reference toMerge = references.get(entry.getKey());
9396
if (null == toMerge) {
9497
references.put(entry.getKey(), entry.getValue());
@@ -105,12 +108,13 @@ private static Reference[] generateReferences(Instrumenter.HasMethodAdvice instr
105108
private static byte[] generateMuzzleClass(InstrumenterModule module) {
106109

107110
Set<String> ignoredClassNames = new HashSet<>(Arrays.asList(module.muzzleIgnoredClassNames()));
111+
AdviceShader adviceShader = AdviceShader.with(module.adviceShading());
108112

109113
List<Reference> references = new ArrayList<>();
110114
for (Instrumenter instrumenter : module.typeInstrumentations()) {
111115
if (instrumenter instanceof Instrumenter.HasMethodAdvice) {
112116
for (Reference reference :
113-
generateReferences((Instrumenter.HasMethodAdvice) instrumenter)) {
117+
generateReferences((Instrumenter.HasMethodAdvice) instrumenter, adviceShader)) {
114118
// ignore helper classes, they will be injected by the instrumentation's HelperInjector.
115119
if (!ignoredClassNames.contains(reference.className)) {
116120
references.add(reference);

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVersionScanPlugin.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.agent.tooling.muzzle;
22

3+
import datadog.trace.agent.tooling.AdviceShader;
34
import datadog.trace.agent.tooling.HelperInjector;
45
import datadog.trace.agent.tooling.InstrumenterModule;
56
import datadog.trace.agent.tooling.bytebuddy.SharedTypePools;
@@ -109,6 +110,7 @@ private static Map<String, byte[]> createHelperMap(final InstrumenterModule modu
109110
String[] helperClasses = module.helperClassNames();
110111
final Map<String, byte[]> helperMap = new LinkedHashMap<>(helperClasses.length);
111112
Set<String> helperClassNames = new HashSet<>(Arrays.asList(helperClasses));
113+
AdviceShader adviceShader = AdviceShader.with(module.adviceShading());
112114
for (final String helperName : helperClasses) {
113115
int nestedClassIndex = helperName.lastIndexOf('$');
114116
if (nestedClassIndex > 0) {
@@ -128,7 +130,10 @@ private static Map<String, byte[]> createHelperMap(final InstrumenterModule modu
128130
}
129131
final ClassFileLocator locator =
130132
ClassFileLocator.ForClassLoader.of(module.getClass().getClassLoader());
131-
final byte[] classBytes = locator.locate(helperName).resolve();
133+
byte[] classBytes = locator.locate(helperName).resolve();
134+
if (null != adviceShader) {
135+
classBytes = adviceShader.shade(classBytes);
136+
}
132137
helperMap.put(helperName, classBytes);
133138
}
134139
return helperMap;

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static datadog.trace.util.Strings.getClassName;
44
import static datadog.trace.util.Strings.getResourceName;
55

6+
import datadog.trace.agent.tooling.AdviceShader;
67
import datadog.trace.bootstrap.Constants;
78
import java.io.InputStream;
89
import java.util.ArrayDeque;
@@ -41,12 +42,14 @@ public class ReferenceCreator extends ClassVisitor {
4142
* Generate all references reachable from a given class.
4243
*
4344
* @param entryPointClassName Starting point for generating references.
45+
* @param adviceShader Optional shading to apply to the advice.
4446
* @param loader Classloader used to read class bytes.
4547
* @return Map of [referenceClassName -> Reference]
4648
* @throws IllegalStateException if class is not found or unable to be loaded.
4749
*/
4850
public static Map<String, Reference> createReferencesFrom(
49-
final String entryPointClassName, final ClassLoader loader) throws IllegalStateException {
51+
final String entryPointClassName, final AdviceShader adviceShader, final ClassLoader loader)
52+
throws IllegalStateException {
5053
final Set<String> visitedSources = new HashSet<>();
5154
final Map<String, Reference> references = new LinkedHashMap<>();
5255

@@ -64,7 +67,11 @@ public static Map<String, Reference> createReferencesFrom(
6467
}
6568
final ReferenceCreator cv = new ReferenceCreator(null);
6669
final ClassReader reader = new ClassReader(in);
67-
reader.accept(cv, ClassReader.SKIP_FRAMES);
70+
if (null == adviceShader) {
71+
reader.accept(cv, ClassReader.SKIP_FRAMES);
72+
} else {
73+
reader.accept(adviceShader.shade(cv), ClassReader.SKIP_FRAMES);
74+
}
6875

6976
final Map<String, Reference> instrumentationReferences = cv.getReferences();
7077
for (final Map.Entry<String, Reference> entry : instrumentationReferences.entrySet()) {

dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/AWSHttpClientInstrumentation.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,15 @@
2222
*/
2323
public class AWSHttpClientInstrumentation
2424
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
25+
private final String targetPackage;
26+
27+
public AWSHttpClientInstrumentation(String targetPackage) {
28+
this.targetPackage = targetPackage;
29+
}
2530

2631
@Override
2732
public String instrumentedType() {
28-
return "com.amazonaws.http.AmazonHttpClient";
33+
return targetPackage + ".http.AmazonHttpClient";
2934
}
3035

3136
@Override

dd-java-agent/instrumentation/aws-java-sdk-1.11.0/src/main/java/datadog/trace/instrumentation/aws/v0/AwsSdkModule.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@
99

1010
/** Groups the instrumentations for AWS SDK 1.11.0+. */
1111
@AutoService(InstrumenterModule.class)
12-
public final class AwsSdkModule extends InstrumenterModule.Tracing {
12+
public class AwsSdkModule extends InstrumenterModule.Tracing {
13+
private final String targetPackage;
1314

1415
public AwsSdkModule() {
16+
this("com.amazonaws");
17+
}
18+
19+
public AwsSdkModule(String targetPackage) {
1520
super("aws-sdk");
21+
this.targetPackage = targetPackage;
1622
}
1723

1824
@Override
@@ -30,18 +36,18 @@ public String[] helperClassNames() {
3036
@Override
3137
public Map<String, String> contextStore() {
3238
Map<String, String> map = new java.util.HashMap<>();
33-
map.put("com.amazonaws.services.sqs.model.ReceiveMessageResult", "java.lang.String");
39+
map.put(targetPackage + ".services.sqs.model.ReceiveMessageResult", "java.lang.String");
3440
map.put(
35-
"com.amazonaws.AmazonWebServiceRequest",
41+
targetPackage + ".AmazonWebServiceRequest",
3642
"datadog.trace.bootstrap.instrumentation.api.AgentSpan");
3743
return map;
3844
}
3945

4046
@Override
4147
public List<Instrumenter> typeInstrumentations() {
4248
return Arrays.asList(
43-
new AWSHttpClientInstrumentation(),
44-
new RequestExecutorInstrumentation(),
45-
new HandlerChainFactoryInstrumentation());
49+
new AWSHttpClientInstrumentation(targetPackage),
50+
new RequestExecutorInstrumentation(targetPackage),
51+
new HandlerChainFactoryInstrumentation(targetPackage));
4652
}
4753
}

0 commit comments

Comments
 (0)