Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,6 +29,8 @@ public class SourceFileTrackingTransformer implements ClassFileTransformer {
private final Queue<SourceFileItem> queue = new ConcurrentLinkedQueue<>();
private final AgentTaskScheduler scheduler = AgentTaskScheduler.INSTANCE;
private AgentTaskScheduler.Scheduled<Runnable> scheduled;
// this field MUST only be used in flush() calling thread
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe s/used/initialized/ ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in fact, both initialized and used. as there is no volatile qualifier it is assumed that it will be used in the same thread

private ClassNameFiltering classNameFilter;

public SourceFileTrackingTransformer(ClassesToRetransformFinder finder) {
this.finder = finder;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Class<?>> 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<Class<?>> 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<Class<?>> 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<Class<?>> 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
Expand All @@ -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++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down