From 2952924e7e5678032d1732039ace9658d4e39faf Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Mon, 7 Jul 2025 11:31:25 +0200 Subject: [PATCH] Add source file tracking enable option By default, source file tracking is enabled, but add an option to disable it if there are any issue wit it. only allow java, kotlin, scala and groovy files to be tracked. --- .../agent/ClassesToRetransformFinder.java | 4 +++ .../datadog/debugger/agent/DebuggerAgent.java | 4 +++ .../agent/SourceFileTrackingTransformer.java | 10 ++++++ .../SourceFileTrackingTransformerTest.java | 32 +++++++++++++++++++ .../datadog/trace/api/ConfigDefaults.java | 1 + .../trace/api/config/DebuggerConfig.java | 2 ++ .../main/java/datadog/trace/api/Config.java | 10 ++++++ 7 files changed, 63 insertions(+) diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ClassesToRetransformFinder.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ClassesToRetransformFinder.java index cc92360c7ab..271fbbda594 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ClassesToRetransformFinder.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ClassesToRetransformFinder.java @@ -111,4 +111,8 @@ private static boolean lookupClass(Trie changedClasses, Class clazz) { String simpleName = extractSimpleName(clazz); return changedClasses.contains(reverseStr(simpleName)); } + + ConcurrentMap getClassNamesBySourceFile() { + return classNamesBySourceFile; + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java index 36eafade871..38b600fc12c 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java @@ -324,6 +324,10 @@ private static String getDiagnosticEndpoint( private static void setupSourceFileTracking( Instrumentation instrumentation, ClassesToRetransformFinder finder) { + if (!Config.get().isDebuggerSourceFileTrackingEnabled()) { + LOGGER.debug("Source file tracking is disabled"); + return; + } SourceFileTrackingTransformer sourceFileTrackingTransformer = new SourceFileTrackingTransformer(finder); sourceFileTrackingTransformer.start(); 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 4976ef01c8f..b3df4fc220f 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 @@ -75,6 +75,9 @@ private void registerSourceFile(String className, byte[] classfileBuffer) { if (sourceFile == null) { return; } + if (!isExtensionAllowed(sourceFile)) { + return; + } String simpleClassName = stripPackagePath(className); String simpleSourceFile = removeExtension(sourceFile); if (simpleClassName.equals(simpleSourceFile)) { @@ -83,6 +86,13 @@ private void registerSourceFile(String className, byte[] classfileBuffer) { finder.register(sourceFile, className); } + private boolean isExtensionAllowed(String sourceFile) { + return sourceFile.endsWith(".java") + || sourceFile.endsWith(".kt") + || sourceFile.endsWith(".scala") + || sourceFile.endsWith(".groovy"); + } + private static class SourceFileItem { final String className; final byte[] classfileBuffer; 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 d07e02ad580..44c8f0f101d 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 @@ -79,6 +79,38 @@ void transformInner() throws IllegalClassFormatException { assertEquals(InnerHelper.MySecondInner.class, changedClasses.get(2)); } + @Test + void transformNotAllowed() throws IllegalClassFormatException { + ClassesToRetransformFinder finder = new ClassesToRetransformFinder(); + SourceFileTrackingTransformer sourceFileTrackingTransformer = + new SourceFileTrackingTransformer(finder); + ConfigurationComparer comparer = createComparer("TopLevelHelper.java"); + byte[] classFileBytes = getClassFileBytes(TopLevelHelper.class); + replaceInByteArray( + classFileBytes, "TopLevelHelper.java".getBytes(), "TopLevelHelper.cloj".getBytes()); + sourceFileTrackingTransformer.transform(null, "", null, null, classFileBytes); + sourceFileTrackingTransformer.flush(); + List> changedClasses = + finder.getAllLoadedChangedClasses(new Class[] {InnerHelper.class}, comparer); + 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++) { + if (buffer[i] == oldBytes[oldIdx]) { + oldIdx++; + if (oldIdx == oldBytes.length) { + // Found the oldBytes, replace with newBytes + System.arraycopy(newBytes, 0, buffer, i - oldIdx + 1, newBytes.length); + oldIdx = 0; // Reset for next search + } + } else { + oldIdx = 0; // Reset if current byte does not match + } + } + } + private ConfigurationComparer createComparer(String sourceFile) { Configuration emptyConfig = Configuration.builder().setService("service-name").build(); Configuration newConfig = diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index a6d5888e76f..f5a79493b96 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -207,6 +207,7 @@ public final class ConfigDefaults { static final int DEFAULT_DEBUGGER_EXCEPTION_MAX_CAPTURED_FRAMES = 3; static final int DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS = 60 * 60; static final boolean DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED = false; + static final boolean DEFAULT_DEBUGGER_SOURCE_FILE_TRACKING_ENABLED = true; static final boolean DEFAULT_TRACE_REPORT_HOSTNAME = false; static final String DEFAULT_TRACE_ANNOTATIONS = null; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java index c92d5705962..536821c9a25 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java @@ -61,6 +61,8 @@ public final class DebuggerConfig { public static final String DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED = "exception.replay.capture.intermediate.spans.enabled"; public static final String DISTRIBUTED_DEBUGGER_ENABLED = "distributed.debugger.enabled"; + public static final String DEBUGGER_SOURCE_FILE_TRACKING_ENABLED = + "dynamic.instrumentation.source.file.tracking.enabled"; public static final String THIRD_PARTY_INCLUDES = "third.party.includes"; public static final String THIRD_PARTY_EXCLUDES = "third.party.excludes"; public static final String THIRD_PARTY_SHADING_IDENTIFIERS = "third.party.shading.identifiers"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 8f1c06837ce..51ee1ac6966 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -60,6 +60,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_MAX_CAPTURED_FRAMES; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_ONLY_LOCAL_ROOT; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_MAX_EXCEPTION_PER_SECOND; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_SOURCE_FILE_TRACKING_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DOGSTATSD_PORT; import static datadog.trace.api.ConfigDefaults.DEFAULT_DOGSTATSD_START_DELAY; @@ -281,6 +282,7 @@ import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_MAX_CAPTURED_FRAMES; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_ONLY_LOCAL_ROOT; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_MAX_EXCEPTION_PER_SECOND; +import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_SOURCE_FILE_TRACKING_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DISTRIBUTED_DEBUGGER_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED; @@ -1048,6 +1050,7 @@ public static String getHostName() { private final boolean debuggerCodeOriginEnabled; private final int debuggerCodeOriginMaxUserFrames; private final boolean distributedDebuggerEnabled; + private final boolean debuggerSourceFileTrackingEnabled; private final Set debuggerThirdPartyIncludes; private final Set debuggerThirdPartyExcludes; @@ -2385,6 +2388,9 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.getInteger( DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS, DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS); + debuggerSourceFileTrackingEnabled = + configProvider.getBoolean( + DEBUGGER_SOURCE_FILE_TRACKING_ENABLED, DEFAULT_DEBUGGER_SOURCE_FILE_TRACKING_ENABLED); debuggerThirdPartyIncludes = tryMakeImmutableSet(configProvider.getList(THIRD_PARTY_INCLUDES)); debuggerThirdPartyExcludes = tryMakeImmutableSet(configProvider.getList(THIRD_PARTY_EXCLUDES)); @@ -3988,6 +3994,10 @@ public boolean isDistributedDebuggerEnabled() { return distributedDebuggerEnabled; } + public boolean isDebuggerSourceFileTrackingEnabled() { + return debuggerSourceFileTrackingEnabled; + } + public Set getThirdPartyIncludes() { return debuggerThirdPartyIncludes; }