Skip to content

Commit 5140087

Browse files
committed
Add support for instrumentation based class injection via dd-instrument-java
1 parent 1128695 commit 5140087

File tree

6 files changed

+47
-6
lines changed

6 files changed

+47
-6
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static net.bytebuddy.matcher.ElementMatchers.isDefaultFinalizer;
77

88
import datadog.environment.SystemProperties;
9+
import datadog.instrument.classinject.ClassInjector;
910
import datadog.trace.agent.tooling.bytebuddy.SharedTypePools;
1011
import datadog.trace.agent.tooling.bytebuddy.iast.TaintableRedefinitionStrategyListener;
1112
import datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers;
@@ -108,6 +109,10 @@ public static ClassFileTransformer installBytebuddyAgent(
108109
final AgentBuilder.Listener... listeners) {
109110
Utils.setInstrumentation(inst);
110111

112+
if (!InstrumenterConfig.get().isUnsafeClassInjection()) {
113+
ClassInjector.enableClassInjection(inst);
114+
}
115+
111116
TypePoolFacade.registerAsSupplier();
112117

113118
if (InstrumenterConfig.get().isResolverMemoizingEnabled()) {

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import static datadog.trace.bootstrap.AgentClassLoading.INJECTING_HELPERS;
44
import static java.util.Arrays.asList;
55

6+
import datadog.trace.api.InstrumenterConfig;
67
import datadog.trace.bootstrap.instrumentation.api.EagerHelper;
78
import datadog.trace.util.JDK9ModuleAccess;
89
import java.io.IOException;
910
import java.lang.ref.WeakReference;
1011
import java.lang.reflect.AnnotatedElement;
1112
import java.security.CodeSource;
1213
import java.security.ProtectionDomain;
14+
import java.util.Collection;
1315
import java.util.Collections;
1416
import java.util.HashSet;
1517
import java.util.LinkedHashMap;
@@ -34,6 +36,9 @@ public class HelperInjector implements Instrumenter.TransformingAdvice {
3436
private static final ClassFileLocator classFileLocator =
3537
ClassFileLocator.ForClassLoader.of(Utils.getExtendedClassLoader());
3638

39+
private static final boolean unsafeClassInjection =
40+
InstrumenterConfig.get().isUnsafeClassInjection();
41+
3742
private final boolean useAgentCodeSource;
3843
private final AdviceShader adviceShader;
3944
private final String requestingName;
@@ -132,7 +137,7 @@ public DynamicType.Builder<?> transform(
132137
}
133138

134139
final Map<String, byte[]> classnameToBytes = getHelperMap();
135-
final Map<String, Class<?>> classes = injectClassLoader(classLoader, classnameToBytes);
140+
final Collection<Class<?>> classes = injectClassLoader(classLoader, classnameToBytes);
136141

137142
// all datadog helper classes are in the unnamed module
138143
// and there's exactly one unnamed module per classloader
@@ -141,7 +146,7 @@ public DynamicType.Builder<?> transform(
141146
}
142147

143148
// forcibly initialize any eager helpers
144-
for (Class<?> clazz : classes.values()) {
149+
for (Class<?> clazz : classes) {
145150
if (EagerHelper.class.isAssignableFrom(clazz)) {
146151
try {
147152
clazz.getMethod("init").invoke(null);
@@ -173,16 +178,29 @@ public DynamicType.Builder<?> transform(
173178
return builder;
174179
}
175180

176-
private Map<String, Class<?>> injectClassLoader(
181+
private Collection<Class<?>> injectClassLoader(
177182
final ClassLoader classLoader, final Map<String, byte[]> classnameToBytes) {
178183
INJECTING_HELPERS.begin();
179184
try {
180185
if (useAgentCodeSource) {
181186
ProtectionDomain protectionDomain = createProtectionDomain(classLoader);
182-
return new ClassInjector.UsingReflection(classLoader, protectionDomain)
183-
.injectRaw(classnameToBytes);
187+
if (unsafeClassInjection) {
188+
return new ClassInjector.UsingReflection(classLoader, protectionDomain)
189+
.injectRaw(classnameToBytes)
190+
.values();
191+
} else {
192+
return datadog.instrument.classinject.ClassInjector.injectClasses(
193+
classnameToBytes, protectionDomain);
194+
}
184195
} else {
185-
return new ClassInjector.UsingReflection(classLoader).injectRaw(classnameToBytes);
196+
if (unsafeClassInjection) {
197+
return new ClassInjector.UsingReflection(classLoader)
198+
.injectRaw(classnameToBytes)
199+
.values();
200+
} else {
201+
return datadog.instrument.classinject.ClassInjector.injectClasses(
202+
classnameToBytes, classLoader);
203+
}
186204
}
187205
} finally {
188206
INJECTING_HELPERS.end();

dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package datadog.trace.agent.test
22

3+
import datadog.instrument.classinject.ClassInjector
34
import datadog.trace.agent.tooling.HelperInjector
45
import datadog.trace.agent.tooling.Utils
56
import datadog.trace.test.util.DDSpecification
7+
import net.bytebuddy.agent.ByteBuddyAgent
68

79
import java.lang.ref.WeakReference
810
import java.util.concurrent.atomic.AtomicReference
@@ -15,6 +17,7 @@ class HelperInjectionTest extends DDSpecification {
1517

1618
def "helpers injected to non-delegating classloader"() {
1719
setup:
20+
ClassInjector.enableClassInjection(ByteBuddyAgent.getInstrumentation())
1821
HelperInjector injector = new HelperInjector(false, "test", HELPER_CLASS_NAME)
1922
AtomicReference<URLClassLoader> emptyLoader = new AtomicReference<>(new URLClassLoader(new URL[0], (ClassLoader) null))
2023

dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/csi/CallSiteTransformerTest.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package datadog.trace.agent.tooling.csi
22

3+
import datadog.instrument.classinject.ClassInjector
34
import datadog.trace.agent.tooling.bytebuddy.csi.Advices
45
import datadog.trace.agent.tooling.bytebuddy.csi.CallSiteTransformer
56
import datadog.trace.agent.tooling.csi.CallSiteAdvice.MethodHandler
67
import datadog.trace.api.function.TriFunction
78
import groovy.transform.CompileDynamic
9+
import net.bytebuddy.agent.ByteBuddyAgent
810
import net.bytebuddy.description.type.TypeDescription
911
import net.bytebuddy.dynamic.DynamicType
1012
import net.bytebuddy.jar.asm.Opcodes
@@ -206,6 +208,7 @@ class CallSiteTransformerTest extends BaseCallSiteTest {
206208
@SuppressWarnings(['GroovyAccessibility', 'GroovyAssignabilityCheck'])
207209
void 'test call site transformer with helpers'() {
208210
setup:
211+
ClassInjector.enableClassInjection(ByteBuddyAgent.getInstrumentation())
209212
final source = StringConcatExample
210213
final helper = InstrumentationHelper
211214
final customClassLoader = new ClassLoader() { }

dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ public final class TraceInstrumentationConfig {
159159
public static final String RESOLVER_USE_URL_CACHES = "resolver.use.url.caches";
160160
public static final String RESOLVER_RESET_INTERVAL = "resolver.reset.interval";
161161
public static final String RESOLVER_NAMES_ARE_UNIQUE = "resolver.names.are.unique";
162+
163+
public static final String UNSAFE_CLASS_INJECTION = "unsafe.class.injection";
164+
162165
public static final String CASSANDRA_KEYSPACE_STATEMENT_EXTRACTION_ENABLED =
163166
"trace.cassandra.keyspace.statement.extraction.enabled";
164167
public static final String COUCHBASE_INTERNAL_SPANS_ENABLED =

internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_PEKKO_SCHEDULER_ENABLED;
7474
import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_THREAD_POOL_EXECUTORS_EXCLUDE;
7575
import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_WEBSOCKET_MESSAGES_ENABLED;
76+
import static datadog.trace.api.config.TraceInstrumentationConfig.UNSAFE_CLASS_INJECTION;
7677
import static datadog.trace.api.config.UsmConfig.USM_ENABLED;
7778
import static datadog.trace.util.CollectionUtils.tryMakeImmutableList;
7879
import static datadog.trace.util.CollectionUtils.tryMakeImmutableSet;
@@ -162,6 +163,8 @@ public class InstrumenterConfig {
162163
private final Boolean resolverUseUrlCaches;
163164
private final int resolverResetInterval;
164165

166+
private final boolean unsafeClassInjection;
167+
165168
private final boolean runtimeContextFieldInjection;
166169
private final boolean serialVersionUIDFieldInjection;
167170

@@ -280,6 +283,8 @@ private InstrumenterConfig() {
280283
? 0
281284
: configProvider.getInteger(RESOLVER_RESET_INTERVAL, DEFAULT_RESOLVER_RESET_INTERVAL);
282285

286+
unsafeClassInjection = configProvider.getBoolean(UNSAFE_CLASS_INJECTION, false);
287+
283288
runtimeContextFieldInjection =
284289
configProvider.getBoolean(
285290
RUNTIME_CONTEXT_FIELD_INJECTION, DEFAULT_RUNTIME_CONTEXT_FIELD_INJECTION);
@@ -505,6 +510,10 @@ public String getResolverCacheDir() {
505510
return resolverCacheDir;
506511
}
507512

513+
public boolean isUnsafeClassInjection() {
514+
return unsafeClassInjection;
515+
}
516+
508517
public String getInstrumentationConfigId() {
509518
return instrumentationConfigId;
510519
}

0 commit comments

Comments
 (0)