Skip to content

Commit 000e56e

Browse files
authored
Add third-party filtering in SourceFile tracking (#9205)
Initialize ClassNameFiltering in background thread to avoid startup latency
1 parent 7b6259d commit 000e56e

File tree

3 files changed

+119
-61
lines changed

3 files changed

+119
-61
lines changed

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/SourceFileTrackingTransformer.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import static com.datadog.debugger.util.ClassFileHelper.stripPackagePath;
55

66
import com.datadog.debugger.util.ClassFileHelper;
7+
import com.datadog.debugger.util.ClassNameFiltering;
8+
import datadog.trace.api.Config;
79
import datadog.trace.util.AgentTaskScheduler;
10+
import datadog.trace.util.Strings;
811
import java.lang.instrument.ClassFileTransformer;
912
import java.lang.instrument.IllegalClassFormatException;
1013
import java.security.ProtectionDomain;
@@ -26,6 +29,8 @@ public class SourceFileTrackingTransformer implements ClassFileTransformer {
2629
private final Queue<SourceFileItem> queue = new ConcurrentLinkedQueue<>();
2730
private final AgentTaskScheduler scheduler = AgentTaskScheduler.INSTANCE;
2831
private AgentTaskScheduler.Scheduled<Runnable> scheduled;
32+
// this field MUST only be used in flush() calling thread
33+
private ClassNameFiltering classNameFilter;
2934

3035
public SourceFileTrackingTransformer(ClassesToRetransformFinder finder) {
3136
this.finder = finder;
@@ -42,6 +47,11 @@ public void stop() {
4247
}
4348

4449
void flush() {
50+
if (classNameFilter == null) {
51+
// init class name filter once here to parse the config in background thread and avoid
52+
// startup latency on main thread. The field classNameFilter MUST only be used in this thread
53+
classNameFilter = new ClassNameFiltering(Config.get());
54+
}
4555
if (queue.isEmpty()) {
4656
return;
4757
}
@@ -71,6 +81,10 @@ public byte[] transform(
7181
}
7282

7383
private void registerSourceFile(String className, byte[] classfileBuffer) {
84+
String javaClassName = Strings.getClassName(className);
85+
if (classNameFilter.isExcluded(javaClassName)) {
86+
return;
87+
}
7488
String sourceFile = ClassFileHelper.extractSourceFile(classfileBuffer);
7589
if (sourceFile == null) {
7690
return;

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SourceFileTrackingTransformerTest.java

Lines changed: 104 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,79 +4,106 @@
44
import static utils.TestClassFileHelper.getClassFileBytes;
55

66
import com.datadog.debugger.probe.LogProbe;
7+
import datadog.trace.api.Config;
78
import java.lang.instrument.IllegalClassFormatException;
9+
import java.util.Arrays;
10+
import java.util.Collections;
811
import java.util.HashMap;
12+
import java.util.HashSet;
913
import java.util.List;
1014
import org.junit.jupiter.api.Test;
15+
import utils.TestHelper;
1116

1217
class SourceFileTrackingTransformerTest {
1318
@Test
1419
void transformTopLevel() throws IllegalClassFormatException {
15-
ClassesToRetransformFinder finder = new ClassesToRetransformFinder();
16-
SourceFileTrackingTransformer sourceFileTrackingTransformer =
17-
new SourceFileTrackingTransformer(finder);
18-
ConfigurationComparer comparer = createComparer("TopLevelHelper.java");
19-
sourceFileTrackingTransformer.transform(
20-
null,
21-
getInternalName(TopLevelHelper.class),
22-
null,
23-
null,
24-
getClassFileBytes(TopLevelHelper.class));
25-
List<Class<?>> changedClasses =
26-
finder.getAllLoadedChangedClasses(new Class[] {TopLevelHelper.class}, comparer);
27-
assertEquals(1, changedClasses.size());
28-
assertEquals(TopLevelHelper.class, changedClasses.get(0));
29-
sourceFileTrackingTransformer.transform(
30-
null,
31-
getInternalName(MyTopLevelClass.class),
32-
null,
33-
null,
34-
getClassFileBytes(MyTopLevelClass.class));
35-
sourceFileTrackingTransformer.flush();
36-
changedClasses =
37-
finder.getAllLoadedChangedClasses(
38-
new Class[] {TopLevelHelper.class, MyTopLevelClass.class}, comparer);
39-
assertEquals(2, changedClasses.size());
40-
assertEquals(TopLevelHelper.class, changedClasses.get(0));
41-
assertEquals(MyTopLevelClass.class, changedClasses.get(1));
20+
TestHelper.setFieldInConfig(
21+
Config.get(),
22+
"debuggerThirdPartyExcludes",
23+
new HashSet<>(Arrays.asList("com.datadog.debugger.agent")));
24+
try {
25+
ClassesToRetransformFinder finder = new ClassesToRetransformFinder();
26+
SourceFileTrackingTransformer sourceFileTrackingTransformer =
27+
new SourceFileTrackingTransformer(finder);
28+
ConfigurationComparer comparer = createComparer("TopLevelHelper.java");
29+
sourceFileTrackingTransformer.transform(
30+
null,
31+
getInternalName(TopLevelHelper.class),
32+
null,
33+
null,
34+
getClassFileBytes(TopLevelHelper.class));
35+
List<Class<?>> changedClasses =
36+
finder.getAllLoadedChangedClasses(new Class[] {TopLevelHelper.class}, comparer);
37+
assertEquals(1, changedClasses.size());
38+
assertEquals(TopLevelHelper.class, changedClasses.get(0));
39+
sourceFileTrackingTransformer.transform(
40+
null,
41+
getInternalName(MyTopLevelClass.class),
42+
null,
43+
null,
44+
getClassFileBytes(MyTopLevelClass.class));
45+
sourceFileTrackingTransformer.flush();
46+
changedClasses =
47+
finder.getAllLoadedChangedClasses(
48+
new Class[] {TopLevelHelper.class, MyTopLevelClass.class}, comparer);
49+
assertEquals(2, changedClasses.size());
50+
assertEquals(TopLevelHelper.class, changedClasses.get(0));
51+
assertEquals(MyTopLevelClass.class, changedClasses.get(1));
52+
} finally {
53+
TestHelper.setFieldInConfig(
54+
Config.get(), "debuggerThirdPartyExcludes", Collections.emptySet());
55+
}
4256
}
4357

4458
@Test
4559
void transformInner() throws IllegalClassFormatException {
46-
ClassesToRetransformFinder finder = new ClassesToRetransformFinder();
47-
SourceFileTrackingTransformer sourceFileTrackingTransformer =
48-
new SourceFileTrackingTransformer(finder);
49-
ConfigurationComparer comparer = createComparer("InnerHelper.java");
50-
sourceFileTrackingTransformer.transform(
51-
null, getInternalName(InnerHelper.class), null, null, getClassFileBytes(InnerHelper.class));
52-
sourceFileTrackingTransformer.flush();
53-
List<Class<?>> changedClasses =
54-
finder.getAllLoadedChangedClasses(new Class[] {InnerHelper.class}, comparer);
55-
assertEquals(1, changedClasses.size());
56-
assertEquals(InnerHelper.class, changedClasses.get(0));
57-
sourceFileTrackingTransformer.transform(
58-
null,
59-
getInternalName(InnerHelper.MyInner.class),
60-
null,
61-
null,
62-
getClassFileBytes(InnerHelper.MyInner.class));
63-
sourceFileTrackingTransformer.transform(
64-
null,
65-
getInternalName(InnerHelper.MySecondInner.class),
66-
null,
67-
null,
68-
getClassFileBytes(InnerHelper.MySecondInner.class));
69-
sourceFileTrackingTransformer.flush();
70-
changedClasses =
71-
finder.getAllLoadedChangedClasses(
72-
new Class[] {
73-
InnerHelper.class, InnerHelper.MyInner.class, InnerHelper.MySecondInner.class
74-
},
75-
comparer);
76-
assertEquals(3, changedClasses.size());
77-
assertEquals(InnerHelper.class, changedClasses.get(0));
78-
assertEquals(InnerHelper.MyInner.class, changedClasses.get(1));
79-
assertEquals(InnerHelper.MySecondInner.class, changedClasses.get(2));
60+
TestHelper.setFieldInConfig(
61+
Config.get(),
62+
"debuggerThirdPartyExcludes",
63+
new HashSet<>(Arrays.asList("com.datadog.debugger.agent")));
64+
try {
65+
ClassesToRetransformFinder finder = new ClassesToRetransformFinder();
66+
SourceFileTrackingTransformer sourceFileTrackingTransformer =
67+
new SourceFileTrackingTransformer(finder);
68+
ConfigurationComparer comparer = createComparer("InnerHelper.java");
69+
sourceFileTrackingTransformer.transform(
70+
null,
71+
getInternalName(InnerHelper.class),
72+
null,
73+
null,
74+
getClassFileBytes(InnerHelper.class));
75+
sourceFileTrackingTransformer.flush();
76+
List<Class<?>> changedClasses =
77+
finder.getAllLoadedChangedClasses(new Class[] {InnerHelper.class}, comparer);
78+
assertEquals(1, changedClasses.size());
79+
assertEquals(InnerHelper.class, changedClasses.get(0));
80+
sourceFileTrackingTransformer.transform(
81+
null,
82+
getInternalName(InnerHelper.MyInner.class),
83+
null,
84+
null,
85+
getClassFileBytes(InnerHelper.MyInner.class));
86+
sourceFileTrackingTransformer.transform(
87+
null,
88+
getInternalName(InnerHelper.MySecondInner.class),
89+
null,
90+
null,
91+
getClassFileBytes(InnerHelper.MySecondInner.class));
92+
sourceFileTrackingTransformer.flush();
93+
changedClasses =
94+
finder.getAllLoadedChangedClasses(
95+
new Class[] {
96+
InnerHelper.class, InnerHelper.MyInner.class, InnerHelper.MySecondInner.class
97+
},
98+
comparer);
99+
assertEquals(3, changedClasses.size());
100+
assertEquals(InnerHelper.class, changedClasses.get(0));
101+
assertEquals(InnerHelper.MyInner.class, changedClasses.get(1));
102+
assertEquals(InnerHelper.MySecondInner.class, changedClasses.get(2));
103+
} finally {
104+
TestHelper.setFieldInConfig(
105+
Config.get(), "debuggerThirdPartyExcludes", Collections.emptySet());
106+
}
80107
}
81108

82109
@Test
@@ -95,6 +122,22 @@ void transformNotAllowed() throws IllegalClassFormatException {
95122
assertEquals(0, finder.getClassNamesBySourceFile().size());
96123
}
97124

125+
@Test
126+
void thirdPartyExcludes() throws IllegalClassFormatException {
127+
ClassesToRetransformFinder finder = new ClassesToRetransformFinder();
128+
SourceFileTrackingTransformer sourceFileTrackingTransformer =
129+
new SourceFileTrackingTransformer(finder);
130+
ConfigurationComparer comparer = createComparer("TopLevelHelper.java");
131+
sourceFileTrackingTransformer.transform(
132+
null,
133+
getInternalName(TopLevelHelper.class),
134+
null,
135+
null,
136+
getClassFileBytes(TopLevelHelper.class));
137+
sourceFileTrackingTransformer.flush();
138+
assertEquals(0, finder.getClassNamesBySourceFile().size());
139+
}
140+
98141
private static void replaceInByteArray(byte[] buffer, byte[] oldBytes, byte[] newBytes) {
99142
int oldIdx = 0;
100143
for (int i = 0; i < buffer.length; i++) {

dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/InProductEnablementIntegrationTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ void testDynamicInstrumentationEnablement() throws Exception {
4040
@Test
4141
@DisplayName("testDynamicInstrumentationEnablementWithLineProbe")
4242
void testDynamicInstrumentationEnablementWithLineProbe() throws Exception {
43+
additionalJvmArgs.add("-Ddd.third.party.excludes=datadog.smoketest");
4344
appUrl = startAppAndAndGetUrl();
4445
setConfigOverrides(createConfigOverrides(true, false));
4546
LogProbe probe =

0 commit comments

Comments
 (0)