Ensure injected modules can read the helper class module
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java
@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -33,6 +34,7 @@ public class HelperInjector implements Transformer {
private final Set<String> helperClassNames;
private Map<TypeDescription, byte[]> helperMap = null;
private final WeakMap<ClassLoader, Boolean> injectedClassLoaders = newWeakMap();
+ private final Set<JavaModule> helperModules = new HashSet<>();
/**
* Construct HelperInjector.
@@ -88,7 +90,7 @@ public class HelperInjector implements Transformer {
final TypeDescription typeDescription,
ClassLoader classLoader,
final JavaModule module) {
- if (helperClassNames.size() > 0) {
+ if (!helperClassNames.isEmpty()) {
synchronized (this) {
if (classLoader == BOOTSTRAP_CLASSLOADER) {
classLoader = BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
@@ -105,10 +107,21 @@ public class HelperInjector implements Transformer {
AgentInstaller.getInstrumentation())
.inject(helperMap);
for (final TypeDescription desc : injected.keySet()) {
+ final Class<?> injectedClass =
Class.forName(desc.getName(), false, Utils.getBootstrapProxy());
+ if (JavaModule.isSupported()) {
+ helperModules.add(JavaModule.ofType(injectedClass));
+ }
}
} else {
+ final Map<TypeDescription, Class<?>> classMap =
new ClassInjector.UsingReflection(classLoader).inject(helperMap);
+
+ if (JavaModule.isSupported()) {
+ for (final Class<?> injectedClass : classMap.values()) {
+ helperModules.add(JavaModule.ofType(injectedClass));
+ }
+ }
}
} catch (final Exception e) {
log.error(
@@ -125,8 +138,21 @@ public class HelperInjector implements Transformer {
}
injectedClassLoaders.put(classLoader, true);
}
+
+ ensureModuleCanReadHelperModules(module);
}
}
return builder;
}
+
+ private void ensureModuleCanReadHelperModules(final JavaModule target) {
+ if (JavaModule.isSupported() && target != JavaModule.UNSUPPORTED && target.isNamed()) {
+ for (final JavaModule helperModule : helperModules) {
+ if (!target.canRead(helperModule)) {
+ log.debug("Adding module read from {} to {}", target, helperModule);
+ target.addReads(AgentInstaller.getInstrumentation(), helperModule);
+ }
+ }
+ }
+ }
}

ADD
dd-java-agent/instrumentation/java-concurrent/src/test/groovy/ModuleInjectionTest.groovy
+import datadog.trace.agent.test.AgentTestRunner
+
+import javax.swing.*
+
+/**
+ * This class tests that we correctly add module references when instrumenting
+ */
+class ModuleInjectionTest extends AgentTestRunner {
+ /**
+ * There's nothing special about RepaintManager other than
+ * it's in a module (java.desktop) that doesn't read the "unnamed module" and it
+ * creates an instrumented runnable in its constructor
+ */
+ def "test instrumenting java.desktop class"() {
+ when:
+ RepaintManager repaintManager = new RepaintManager()
+
+ then:
+ noExceptionThrown()
+ }
+}

Ensure injected modules can read the helper class module