Skip to content

Commit f77cce0

Browse files
Merge pull request DataDog#1126 from DataDog/landerson/helper-injector-optimization
HelperInjector optimization
2 parents d74cf29 + 42ac5fc commit f77cce0

1 file changed

Lines changed: 62 additions & 69 deletions

File tree

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

Lines changed: 62 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.io.IOException;
99
import java.lang.ref.WeakReference;
1010
import java.security.SecureClassLoader;
11-
import java.util.ArrayList;
1211
import java.util.Arrays;
1312
import java.util.Collection;
1413
import java.util.Collections;
@@ -18,6 +17,7 @@
1817
import java.util.List;
1918
import java.util.Map;
2019
import java.util.Set;
20+
import java.util.concurrent.CopyOnWriteArrayList;
2121
import lombok.extern.slf4j.Slf4j;
2222
import net.bytebuddy.agent.builder.AgentBuilder.Transformer;
2323
import net.bytebuddy.description.type.TypeDescription;
@@ -34,12 +34,11 @@ public class HelperInjector implements Transformer {
3434
new SecureClassLoader(null) {};
3535

3636
private final Set<String> helperClassNames;
37-
private Map<TypeDescription, byte[]> helperMap = null;
38-
private final WeakMap<ClassLoader, Boolean> injectedClassLoaders = newWeakMap();
37+
private final Map<String, byte[]> dynamicTypeMap = new LinkedHashMap<>();
3938

40-
// Neither Module nor WeakReference implements equals or hashcode so using a list
41-
private final List<WeakReference<Object>> helperModules = new ArrayList<>();
39+
private final WeakMap<ClassLoader, Boolean> injectedClassLoaders = newWeakMap();
4240

41+
private final List<WeakReference<Object>> helperModules = new CopyOnWriteArrayList<>();
4342
/**
4443
* Construct HelperInjector.
4544
*
@@ -55,13 +54,7 @@ public HelperInjector(final String... helperClassNames) {
5554

5655
public HelperInjector(final Map<String, byte[]> helperMap) {
5756
helperClassNames = helperMap.keySet();
58-
this.helperMap = new LinkedHashMap<>(helperClassNames.size());
59-
for (final String helperName : helperClassNames) {
60-
final TypeDescription typeDesc =
61-
new TypeDescription.Latent(
62-
helperName, 0, null, Collections.<TypeDescription.Generic>emptyList());
63-
this.helperMap.put(typeDesc, helperMap.get(helperName));
64-
}
57+
dynamicTypeMap.putAll(helperMap);
6558
}
6659

6760
public static HelperInjector forDynamicTypes(final Collection<DynamicType.Unloaded<?>> helpers) {
@@ -72,20 +65,22 @@ public static HelperInjector forDynamicTypes(final Collection<DynamicType.Unload
7265
return new HelperInjector(bytes);
7366
}
7467

75-
private synchronized Map<TypeDescription, byte[]> getHelperMap() throws IOException {
76-
if (helperMap == null) {
77-
helperMap = new LinkedHashMap<>(helperClassNames.size());
78-
for (final String helperName : helperClassNames) {
79-
final ClassFileLocator locator =
80-
ClassFileLocator.ForClassLoader.of(Utils.getAgentClassLoader());
81-
final byte[] classBytes = locator.locate(helperName).resolve();
82-
final TypeDescription typeDesc =
83-
new TypeDescription.Latent(
84-
helperName, 0, null, Collections.<TypeDescription.Generic>emptyList());
85-
helperMap.put(typeDesc, classBytes);
68+
private Map<String, byte[]> getHelperMap() throws IOException {
69+
if (dynamicTypeMap.isEmpty()) {
70+
final Map<String, byte[]> classnameToBytes = new LinkedHashMap<>();
71+
72+
final ClassFileLocator locator =
73+
ClassFileLocator.ForClassLoader.of(Utils.getAgentClassLoader());
74+
75+
for (final String helperClassName : helperClassNames) {
76+
final byte[] classBytes = locator.locate(helperClassName).resolve();
77+
classnameToBytes.put(helperClassName, classBytes);
8678
}
79+
80+
return classnameToBytes;
81+
} else {
82+
return dynamicTypeMap;
8783
}
88-
return helperMap;
8984
}
9085

9186
@Override
@@ -95,55 +90,53 @@ public DynamicType.Builder<?> transform(
9590
ClassLoader classLoader,
9691
final JavaModule module) {
9792
if (!helperClassNames.isEmpty()) {
98-
synchronized (this) {
99-
if (classLoader == BOOTSTRAP_CLASSLOADER) {
100-
classLoader = BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
101-
}
102-
if (!injectedClassLoaders.containsKey(classLoader)) {
103-
try {
104-
final Map<TypeDescription, byte[]> helperMap = getHelperMap();
105-
log.debug("Injecting classes onto classloader {} -> {}", classLoader, helperClassNames);
106-
if (classLoader == BOOTSTRAP_CLASSLOADER_PLACEHOLDER) {
107-
final Map<TypeDescription, Class<?>> injected =
108-
ClassInjector.UsingInstrumentation.of(
109-
new File(System.getProperty("java.io.tmpdir")),
110-
ClassInjector.UsingInstrumentation.Target.BOOTSTRAP,
111-
AgentInstaller.getInstrumentation())
112-
.inject(helperMap);
113-
for (final TypeDescription desc : injected.keySet()) {
114-
final Class<?> injectedClass =
115-
Class.forName(desc.getName(), false, Utils.getBootstrapProxy());
116-
if (JavaModule.isSupported()) {
117-
helperModules.add(new WeakReference<>(JavaModule.ofType(injectedClass).unwrap()));
118-
}
119-
}
120-
} else {
121-
final Map<TypeDescription, Class<?>> classMap =
122-
new ClassInjector.UsingReflection(classLoader).inject(helperMap);
123-
if (JavaModule.isSupported()) {
124-
for (final Class<?> injectedClass : classMap.values()) {
125-
helperModules.add(new WeakReference<>(JavaModule.ofType(injectedClass).unwrap()));
126-
}
127-
}
128-
}
129-
} catch (final Exception e) {
130-
log.error(
131-
"Error preparing helpers for "
132-
+ typeDescription
133-
+ ". Failed to inject helper classes into instance "
134-
+ classLoader
135-
+ " of type "
136-
+ (classLoader == BOOTSTRAP_CLASSLOADER_PLACEHOLDER
137-
? "<bootstrap>"
138-
: classLoader.getClass().getName()),
139-
e);
140-
throw new RuntimeException(e);
93+
if (classLoader == BOOTSTRAP_CLASSLOADER) {
94+
classLoader = BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
95+
}
96+
97+
if (!injectedClassLoaders.containsKey(classLoader)) {
98+
try {
99+
log.debug("Injecting classes onto classloader {} -> {}", classLoader, helperClassNames);
100+
101+
final Map<String, byte[]> classnameToBytes = getHelperMap();
102+
final Map<String, Class<?>> classes;
103+
if (classLoader == BOOTSTRAP_CLASSLOADER_PLACEHOLDER) {
104+
classes =
105+
ClassInjector.UsingInstrumentation.of(
106+
new File(System.getProperty("java.io.tmpdir")),
107+
ClassInjector.UsingInstrumentation.Target.BOOTSTRAP,
108+
AgentInstaller.getInstrumentation())
109+
.injectRaw(classnameToBytes);
110+
} else {
111+
classes = new ClassInjector.UsingReflection(classLoader).injectRaw(classnameToBytes);
141112
}
142-
injectedClassLoaders.put(classLoader, true);
113+
114+
// All datadog helper classes are in the unnamed module
115+
// And there's exactly one unnamed module per classloader
116+
// Use the module of the first class for convenience
117+
if (JavaModule.isSupported()) {
118+
final JavaModule javaModule = JavaModule.ofType(classes.values().iterator().next());
119+
helperModules.add(new WeakReference<>(javaModule.unwrap()));
120+
}
121+
} catch (final Exception e) {
122+
final String classLoaderType =
123+
classLoader == BOOTSTRAP_CLASSLOADER_PLACEHOLDER
124+
? "<bootstrap>"
125+
: classLoader.getClass().getName();
126+
127+
log.error(
128+
"Error preparing helpers for {}. Failed to inject helper classes into instance {} of type {}",
129+
typeDescription,
130+
classLoader,
131+
classLoaderType,
132+
e);
133+
throw new RuntimeException(e);
143134
}
144135

145-
ensureModuleCanReadHelperModules(module);
136+
injectedClassLoaders.put(classLoader, true);
146137
}
138+
139+
ensureModuleCanReadHelperModules(module);
147140
}
148141
return builder;
149142
}

0 commit comments

Comments
 (0)