diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/SourceFileTrackingTransformer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/SourceFileTrackingTransformer.java index b3df4fc220f..1ca248f9dd5 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/SourceFileTrackingTransformer.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/SourceFileTrackingTransformer.java @@ -4,7 +4,10 @@ import static com.datadog.debugger.util.ClassFileHelper.stripPackagePath; import com.datadog.debugger.util.ClassFileHelper; +import com.datadog.debugger.util.ClassNameFiltering; +import datadog.trace.api.Config; import datadog.trace.util.AgentTaskScheduler; +import datadog.trace.util.Strings; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; @@ -26,6 +29,8 @@ public class SourceFileTrackingTransformer implements ClassFileTransformer { private final Queue queue = new ConcurrentLinkedQueue<>(); private final AgentTaskScheduler scheduler = AgentTaskScheduler.INSTANCE; private AgentTaskScheduler.Scheduled scheduled; + // this field MUST only be used in flush() calling thread + private ClassNameFiltering classNameFilter; public SourceFileTrackingTransformer(ClassesToRetransformFinder finder) { this.finder = finder; @@ -42,6 +47,11 @@ public void stop() { } void flush() { + if (classNameFilter == null) { + // init class name filter once here to parse the config in background thread and avoid + // startup latency on main thread. The field classNameFilter MUST only be used in this thread + classNameFilter = new ClassNameFiltering(Config.get()); + } if (queue.isEmpty()) { return; } @@ -71,6 +81,10 @@ public byte[] transform( } private void registerSourceFile(String className, byte[] classfileBuffer) { + String javaClassName = Strings.getClassName(className); + if (classNameFilter.isExcluded(javaClassName)) { + return; + } String sourceFile = ClassFileHelper.extractSourceFile(classfileBuffer); if (sourceFile == null) { return; diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SourceFileTrackingTransformerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SourceFileTrackingTransformerTest.java index 44c8f0f101d..056e3897543 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SourceFileTrackingTransformerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SourceFileTrackingTransformerTest.java @@ -4,79 +4,106 @@ import static utils.TestClassFileHelper.getClassFileBytes; import com.datadog.debugger.probe.LogProbe; +import datadog.trace.api.Config; import java.lang.instrument.IllegalClassFormatException; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import org.junit.jupiter.api.Test; +import utils.TestHelper; class SourceFileTrackingTransformerTest { @Test void transformTopLevel() throws IllegalClassFormatException { - ClassesToRetransformFinder finder = new ClassesToRetransformFinder(); - SourceFileTrackingTransformer sourceFileTrackingTransformer = - new SourceFileTrackingTransformer(finder); - ConfigurationComparer comparer = createComparer("TopLevelHelper.java"); - sourceFileTrackingTransformer.transform( - null, - getInternalName(TopLevelHelper.class), - null, - null, - getClassFileBytes(TopLevelHelper.class)); - List> changedClasses = - finder.getAllLoadedChangedClasses(new Class[] {TopLevelHelper.class}, comparer); - assertEquals(1, changedClasses.size()); - assertEquals(TopLevelHelper.class, changedClasses.get(0)); - sourceFileTrackingTransformer.transform( - null, - getInternalName(MyTopLevelClass.class), - null, - null, - getClassFileBytes(MyTopLevelClass.class)); - sourceFileTrackingTransformer.flush(); - changedClasses = - finder.getAllLoadedChangedClasses( - new Class[] {TopLevelHelper.class, MyTopLevelClass.class}, comparer); - assertEquals(2, changedClasses.size()); - assertEquals(TopLevelHelper.class, changedClasses.get(0)); - assertEquals(MyTopLevelClass.class, changedClasses.get(1)); + TestHelper.setFieldInConfig( + Config.get(), + "debuggerThirdPartyExcludes", + new HashSet<>(Arrays.asList("com.datadog.debugger.agent"))); + try { + ClassesToRetransformFinder finder = new ClassesToRetransformFinder(); + SourceFileTrackingTransformer sourceFileTrackingTransformer = + new SourceFileTrackingTransformer(finder); + ConfigurationComparer comparer = createComparer("TopLevelHelper.java"); + sourceFileTrackingTransformer.transform( + null, + getInternalName(TopLevelHelper.class), + null, + null, + getClassFileBytes(TopLevelHelper.class)); + List> changedClasses = + finder.getAllLoadedChangedClasses(new Class[] {TopLevelHelper.class}, comparer); + assertEquals(1, changedClasses.size()); + assertEquals(TopLevelHelper.class, changedClasses.get(0)); + sourceFileTrackingTransformer.transform( + null, + getInternalName(MyTopLevelClass.class), + null, + null, + getClassFileBytes(MyTopLevelClass.class)); + sourceFileTrackingTransformer.flush(); + changedClasses = + finder.getAllLoadedChangedClasses( + new Class[] {TopLevelHelper.class, MyTopLevelClass.class}, comparer); + assertEquals(2, changedClasses.size()); + assertEquals(TopLevelHelper.class, changedClasses.get(0)); + assertEquals(MyTopLevelClass.class, changedClasses.get(1)); + } finally { + TestHelper.setFieldInConfig( + Config.get(), "debuggerThirdPartyExcludes", Collections.emptySet()); + } } @Test void transformInner() throws IllegalClassFormatException { - ClassesToRetransformFinder finder = new ClassesToRetransformFinder(); - SourceFileTrackingTransformer sourceFileTrackingTransformer = - new SourceFileTrackingTransformer(finder); - ConfigurationComparer comparer = createComparer("InnerHelper.java"); - sourceFileTrackingTransformer.transform( - null, getInternalName(InnerHelper.class), null, null, getClassFileBytes(InnerHelper.class)); - sourceFileTrackingTransformer.flush(); - List> changedClasses = - finder.getAllLoadedChangedClasses(new Class[] {InnerHelper.class}, comparer); - assertEquals(1, changedClasses.size()); - assertEquals(InnerHelper.class, changedClasses.get(0)); - sourceFileTrackingTransformer.transform( - null, - getInternalName(InnerHelper.MyInner.class), - null, - null, - getClassFileBytes(InnerHelper.MyInner.class)); - sourceFileTrackingTransformer.transform( - null, - getInternalName(InnerHelper.MySecondInner.class), - null, - null, - getClassFileBytes(InnerHelper.MySecondInner.class)); - sourceFileTrackingTransformer.flush(); - changedClasses = - finder.getAllLoadedChangedClasses( - new Class[] { - InnerHelper.class, InnerHelper.MyInner.class, InnerHelper.MySecondInner.class - }, - comparer); - assertEquals(3, changedClasses.size()); - assertEquals(InnerHelper.class, changedClasses.get(0)); - assertEquals(InnerHelper.MyInner.class, changedClasses.get(1)); - assertEquals(InnerHelper.MySecondInner.class, changedClasses.get(2)); + TestHelper.setFieldInConfig( + Config.get(), + "debuggerThirdPartyExcludes", + new HashSet<>(Arrays.asList("com.datadog.debugger.agent"))); + try { + ClassesToRetransformFinder finder = new ClassesToRetransformFinder(); + SourceFileTrackingTransformer sourceFileTrackingTransformer = + new SourceFileTrackingTransformer(finder); + ConfigurationComparer comparer = createComparer("InnerHelper.java"); + sourceFileTrackingTransformer.transform( + null, + getInternalName(InnerHelper.class), + null, + null, + getClassFileBytes(InnerHelper.class)); + sourceFileTrackingTransformer.flush(); + List> changedClasses = + finder.getAllLoadedChangedClasses(new Class[] {InnerHelper.class}, comparer); + assertEquals(1, changedClasses.size()); + assertEquals(InnerHelper.class, changedClasses.get(0)); + sourceFileTrackingTransformer.transform( + null, + getInternalName(InnerHelper.MyInner.class), + null, + null, + getClassFileBytes(InnerHelper.MyInner.class)); + sourceFileTrackingTransformer.transform( + null, + getInternalName(InnerHelper.MySecondInner.class), + null, + null, + getClassFileBytes(InnerHelper.MySecondInner.class)); + sourceFileTrackingTransformer.flush(); + changedClasses = + finder.getAllLoadedChangedClasses( + new Class[] { + InnerHelper.class, InnerHelper.MyInner.class, InnerHelper.MySecondInner.class + }, + comparer); + assertEquals(3, changedClasses.size()); + assertEquals(InnerHelper.class, changedClasses.get(0)); + assertEquals(InnerHelper.MyInner.class, changedClasses.get(1)); + assertEquals(InnerHelper.MySecondInner.class, changedClasses.get(2)); + } finally { + TestHelper.setFieldInConfig( + Config.get(), "debuggerThirdPartyExcludes", Collections.emptySet()); + } } @Test @@ -95,6 +122,22 @@ void transformNotAllowed() throws IllegalClassFormatException { assertEquals(0, finder.getClassNamesBySourceFile().size()); } + @Test + void thirdPartyExcludes() throws IllegalClassFormatException { + ClassesToRetransformFinder finder = new ClassesToRetransformFinder(); + SourceFileTrackingTransformer sourceFileTrackingTransformer = + new SourceFileTrackingTransformer(finder); + ConfigurationComparer comparer = createComparer("TopLevelHelper.java"); + sourceFileTrackingTransformer.transform( + null, + getInternalName(TopLevelHelper.class), + null, + null, + getClassFileBytes(TopLevelHelper.class)); + sourceFileTrackingTransformer.flush(); + assertEquals(0, finder.getClassNamesBySourceFile().size()); + } + private static void replaceInByteArray(byte[] buffer, byte[] oldBytes, byte[] newBytes) { int oldIdx = 0; for (int i = 0; i < buffer.length; i++) { diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/InProductEnablementIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/InProductEnablementIntegrationTest.java index d844492ea82..9277d71a968 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/InProductEnablementIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/InProductEnablementIntegrationTest.java @@ -40,6 +40,7 @@ void testDynamicInstrumentationEnablement() throws Exception { @Test @DisplayName("testDynamicInstrumentationEnablementWithLineProbe") void testDynamicInstrumentationEnablementWithLineProbe() throws Exception { + additionalJvmArgs.add("-Ddd.third.party.excludes=datadog.smoketest"); appUrl = startAppAndAndGetUrl(); setConfigOverrides(createConfigOverrides(true, false)); LogProbe probe =