diff --git a/.circleci/config.continue.yml.j2 b/.circleci/config.continue.yml.j2 index a44d1f95e6d..491d9be6ccf 100644 --- a/.circleci/config.continue.yml.j2 +++ b/.circleci/config.continue.yml.j2 @@ -36,7 +36,7 @@ instrumentation_modules: &instrumentation_modules "dd-java-agent/instrumentation debugger_modules: &debugger_modules "dd-java-agent/agent-debugger|dd-java-agent/agent-bootstrap|dd-java-agent/agent-builder|internal-api|communication|dd-trace-core" profiling_modules: &profiling_modules "dd-java-agent/agent-profiling" -default_system_tests_commit: &default_system_tests_commit 0509dbd094c9cbf15f58db96f62276a0adff7efa +default_system_tests_commit: &default_system_tests_commit 6980534f333b3f7a35d83df2230f00f4e26642f5 parameters: nightly: diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index c0b79066e9b..62e5a845dd0 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -96,9 +96,9 @@ private enum AgentFeature { CIVISIBILITY_AGENTLESS(CiVisibilityConfig.CIVISIBILITY_AGENTLESS_ENABLED, false), USM(UsmConfig.USM_ENABLED, false), TELEMETRY(GeneralConfig.TELEMETRY_ENABLED, true), - DEBUGGER(DebuggerConfig.DYNAMIC_INSTRUMENTATION_ENABLED, false), - EXCEPTION_DEBUGGING(DebuggerConfig.EXCEPTION_REPLAY_ENABLED, false), - SPAN_ORIGIN(TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED, false), + DYNAMIC_INSTRUMENTATION(DebuggerConfig.DYNAMIC_INSTRUMENTATION_ENABLED, false), + EXCEPTION_REPLAY(DebuggerConfig.EXCEPTION_REPLAY_ENABLED, false), + CODE_ORIGIN(TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED, false), DATA_JOBS(GeneralConfig.DATA_JOBS_ENABLED, false), AGENTLESS_LOG_SUBMISSION(GeneralConfig.AGENTLESS_LOG_SUBMISSION_ENABLED, false); @@ -149,9 +149,10 @@ public boolean isEnabledByDefault() { private static boolean ciVisibilityEnabled = false; private static boolean usmEnabled = false; private static boolean telemetryEnabled = true; - private static boolean debuggerEnabled = false; - private static boolean exceptionDebuggingEnabled = false; - private static boolean spanOriginEnabled = false; + private static boolean dynamicInstrumentationEnabled = false; + private static boolean exceptionReplayEnabled = false; + private static boolean codeOriginEnabled = false; + private static boolean distributedDebuggerEnabled = false; private static boolean agentlessLogSubmissionEnabled = false; /** @@ -261,9 +262,9 @@ public static void start( || isFeatureEnabled(AgentFeature.DEPRECATED_REMOTE_CONFIG); cwsEnabled = isFeatureEnabled(AgentFeature.CWS); telemetryEnabled = isFeatureEnabled(AgentFeature.TELEMETRY); - debuggerEnabled = isFeatureEnabled(AgentFeature.DEBUGGER); - exceptionDebuggingEnabled = isFeatureEnabled(AgentFeature.EXCEPTION_DEBUGGING); - spanOriginEnabled = isFeatureEnabled(AgentFeature.SPAN_ORIGIN); + dynamicInstrumentationEnabled = isFeatureEnabled(AgentFeature.DYNAMIC_INSTRUMENTATION); + exceptionReplayEnabled = isFeatureEnabled(AgentFeature.EXCEPTION_REPLAY); + codeOriginEnabled = isFeatureEnabled(AgentFeature.CODE_ORIGIN); agentlessLogSubmissionEnabled = isFeatureEnabled(AgentFeature.AGENTLESS_LOG_SUBMISSION); if (profilingEnabled) { @@ -1114,7 +1115,10 @@ private static void shutdownProfilingAgent(final boolean sync) { } private static void maybeStartDebugger(Instrumentation inst, Class scoClass, Object sco) { - if (!debuggerEnabled && !exceptionDebuggingEnabled && !spanOriginEnabled) { + if (isExplicitlyDisabled(DebuggerConfig.DYNAMIC_INSTRUMENTATION_ENABLED) + && isExplicitlyDisabled(DebuggerConfig.EXCEPTION_REPLAY_ENABLED) + && isExplicitlyDisabled(TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED) + && isExplicitlyDisabled(DebuggerConfig.DISTRIBUTED_DEBUGGER_ENABLED)) { return; } if (!remoteConfigEnabled) { @@ -1124,6 +1128,11 @@ private static void maybeStartDebugger(Instrumentation inst, Class scoClass, startDebuggerAgent(inst, scoClass, sco); } + private static boolean isExplicitlyDisabled(String booleanKey) { + return Config.get().configProvider().isSet(booleanKey) + && !Config.get().configProvider().getBoolean(booleanKey); + } + private static synchronized void startDebuggerAgent( Instrumentation inst, Class scoClass, Object sco) { StaticEventLogger.begin("Debugger"); diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java index 6840146f969..c524f065557 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java @@ -50,6 +50,22 @@ public String tag() { public abstract String tag(); } + public interface ProductConfigUpdater { + void updateConfig( + Boolean dynamicInstrumentationEnabled, + Boolean exceptionReplayEnabled, + Boolean codeOriginEnabled, + Boolean liveDebuggingEnabled); + + boolean isDynamicInstrumentationEnabled(); + + boolean isExceptionReplayEnabled(); + + boolean isCodeOriginEnabled(); + + boolean isDistributedDebuggerEnabled(); + } + public interface ProbeResolver { ProbeImplementation resolve(String encodedProbeId); } @@ -103,6 +119,7 @@ public interface CodeOriginRecorder { String captureCodeOrigin(Method method, boolean entry, boolean instrument); } + private static volatile ProductConfigUpdater productConfigUpdater; private static volatile ProbeResolver probeResolver; private static volatile ClassFilter classFilter; private static volatile ClassNameFilter classNameFilter; @@ -112,6 +129,10 @@ public interface CodeOriginRecorder { private static volatile ExceptionDebugger exceptionDebugger; private static volatile CodeOriginRecorder codeOriginRecorder; + public static void initProductConfigUpdater(ProductConfigUpdater productConfigUpdater) { + DebuggerContext.productConfigUpdater = productConfigUpdater; + } + public static void initProbeResolver(ProbeResolver probeResolver) { DebuggerContext.probeResolver = probeResolver; } @@ -144,6 +165,59 @@ public static void initCodeOrigin(CodeOriginRecorder codeOriginRecorder) { DebuggerContext.codeOriginRecorder = codeOriginRecorder; } + public static void updateConfig( + Boolean dynamicInstrumentationEnabled, + Boolean exceptionReplayEnabled, + Boolean codeOriginEnabled, + Boolean liveDebuggingEnabled) { + LOGGER.debug( + "Updating config: dynamicInstrumentationEnabled: {}, exceptionReplayEnabled: {}, codeOriginEnabled: {}, liveDebuggingEnabled: {}", + dynamicInstrumentationEnabled, + exceptionReplayEnabled, + codeOriginEnabled, + liveDebuggingEnabled); + ProductConfigUpdater updater = productConfigUpdater; + if (updater != null) { + updater.updateConfig( + dynamicInstrumentationEnabled, + exceptionReplayEnabled, + codeOriginEnabled, + liveDebuggingEnabled); + } + } + + public static boolean isDynamicInstrumentationEnabled() { + ProductConfigUpdater updater = productConfigUpdater; + if (updater != null) { + return updater.isDynamicInstrumentationEnabled(); + } + return Config.get().isDynamicInstrumentationEnabled(); + } + + public static boolean isExceptionReplayEnabled() { + ProductConfigUpdater updater = productConfigUpdater; + if (updater != null) { + return updater.isExceptionReplayEnabled(); + } + return Config.get().isDebuggerExceptionEnabled(); + } + + public static boolean isCodeOriginEnabled() { + ProductConfigUpdater updater = productConfigUpdater; + if (updater != null) { + return updater.isCodeOriginEnabled(); + } + return Config.get().isDebuggerCodeOriginEnabled(); + } + + public static boolean isDistributedDebuggerEnabled() { + ProductConfigUpdater updater = productConfigUpdater; + if (updater != null) { + return updater.isDistributedDebuggerEnabled(); + } + return Config.get().isDistributedDebuggerEnabled(); + } + /** * Returns the probe details based on the probe id provided. If no probe is found, try to * re-transform the class using the callingClass parameter No-op if no implementation available 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 4b8cef476ca..2a585172849 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 @@ -1,5 +1,7 @@ package com.datadog.debugger.agent; +import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.CODE_ORIGIN; +import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.EXCEPTION; import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.REMOTE_CONFIG; import static datadog.trace.util.AgentThreadFactory.AGENT_THREAD_GROUP; @@ -40,6 +42,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.zip.ZipOutputStream; import org.slf4j.Logger; @@ -48,15 +52,24 @@ /** Debugger agent implementation */ public class DebuggerAgent { private static final Logger LOGGER = LoggerFactory.getLogger(DebuggerAgent.class); + private static Instrumentation instrumentation; private static ConfigurationPoller configurationPoller; private static DebuggerSink sink; private static String agentVersion; private static JsonSnapshotSerializer snapshotSerializer; + private static ClassNameFilter classNameFilter; private static SymDBEnablement symDBEnablement; + private static volatile ConfigurationUpdater configurationUpdater; + private static volatile DefaultExceptionDebugger exceptionDebugger; + static AtomicBoolean dynamicInstrumentationEnabled = new AtomicBoolean(); + static AtomicBoolean exceptionReplayEnabled = new AtomicBoolean(); + static AtomicBoolean codeOriginEnabled = new AtomicBoolean(); + static AtomicBoolean distributedDebuggerEnabled = new AtomicBoolean(); - public static synchronized void run( - Instrumentation instrumentation, SharedCommunicationObjects sco) { + public static synchronized void run(Instrumentation inst, SharedCommunicationObjects sco) { + instrumentation = inst; Config config = Config.get(); + configurationPoller = sco.configurationPoller(config); ClassesToRetransformFinder classesToRetransformFinder = new ClassesToRetransformFinder(); setupSourceFileTracking(instrumentation, classesToRetransformFinder); Redaction.addUserDefinedKeywords(config); @@ -70,8 +83,8 @@ public static synchronized void run( config, diagnosticEndpoint, ddAgentFeaturesDiscovery.supportsDebuggerDiagnostics()); DebuggerSink debuggerSink = createDebuggerSink(config, probeStatusSink); debuggerSink.start(); - ClassNameFilter classNameFilter = new ClassNameFiltering(config); - ConfigurationUpdater configurationUpdater = + classNameFilter = new ClassNameFiltering(config); + configurationUpdater = new ConfigurationUpdater( instrumentation, DebuggerAgent::createTransformer, @@ -81,6 +94,7 @@ public static synchronized void run( sink = debuggerSink; StatsdMetricForwarder statsdMetricForwarder = new StatsdMetricForwarder(config, probeStatusSink); + DebuggerContext.initProductConfigUpdater(new DefaultProductConfigUpdater()); DebuggerContext.initProbeResolver(configurationUpdater); DebuggerContext.initMetricForwarder(statsdMetricForwarder); DebuggerContext.initClassFilter(new DenyListHelper(null)); // default hard coded deny list @@ -88,20 +102,11 @@ public static synchronized void run( DebuggerContext.initValueSerializer(snapshotSerializer); DebuggerContext.initTracer(new DebuggerTracer(debuggerSink.getProbeStatusSink())); DebuggerContext.initClassNameFilter(classNameFilter); - DefaultExceptionDebugger defaultExceptionDebugger = null; if (config.isDebuggerExceptionEnabled()) { - LOGGER.info("Starting Exception Replay"); - defaultExceptionDebugger = - new DefaultExceptionDebugger( - configurationUpdater, - classNameFilter, - Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval()), - config.getDebuggerMaxExceptionPerSecond()); - DebuggerContext.initExceptionDebugger(defaultExceptionDebugger); + startExceptionReplay(); } if (config.isDebuggerCodeOriginEnabled()) { - LOGGER.info("Starting Code Origin for spans"); - DebuggerContext.initCodeOrigin(new DefaultCodeOriginRecorder(config, configurationUpdater)); + startCodeOriginForSpans(); } if (config.isDynamicInstrumentationInstrumentTheWorld()) { setupInstrumentTheWorldTransformer( @@ -109,8 +114,7 @@ public static synchronized void run( } // Dynamic Instrumentation if (config.isDynamicInstrumentationEnabled()) { - startDynamicInstrumentation( - instrumentation, sco, config, configurationUpdater, debuggerSink, classNameFilter); + startDynamicInstrumentation(); } try { /* @@ -121,22 +125,15 @@ public static synchronized void run( } catch (final IllegalStateException ex) { // The JVM is already shutting down. } - ExceptionProbeManager exceptionProbeManager = - defaultExceptionDebugger != null - ? defaultExceptionDebugger.getExceptionProbeManager() - : null; - TracerFlare.addReporter( - new DebuggerReporter(configurationUpdater, sink, exceptionProbeManager)); + TracerFlare.addReporter(DebuggerAgent::addReportToFlare); } - private static void startDynamicInstrumentation( - Instrumentation instrumentation, - SharedCommunicationObjects sco, - Config config, - ConfigurationUpdater configurationUpdater, - DebuggerSink debuggerSink, - ClassNameFilter classNameFilter) { + public static void startDynamicInstrumentation() { + if (!dynamicInstrumentationEnabled.compareAndSet(false, true)) { + return; + } LOGGER.info("Starting Dynamic Instrumentation"); + Config config = Config.get(); String probeFileLocation = config.getDynamicInstrumentationProbeFile(); if (probeFileLocation != null) { Path probeFilePath = Paths.get(probeFileLocation); @@ -144,14 +141,11 @@ private static void startDynamicInstrumentation( probeFilePath, configurationUpdater, config.getDynamicInstrumentationMaxPayloadSize()); return; } - configurationPoller = sco.configurationPoller(config); if (configurationPoller != null) { if (config.isSymbolDatabaseEnabled()) { SymbolAggregator symbolAggregator = new SymbolAggregator( - classNameFilter, - debuggerSink.getSymbolSink(), - config.getSymbolDatabaseFlushThreshold()); + classNameFilter, sink.getSymbolSink(), config.getSymbolDatabaseFlushThreshold()); symbolAggregator.start(); symDBEnablement = new SymDBEnablement(instrumentation, config, symbolAggregator, classNameFilter); @@ -165,6 +159,84 @@ private static void startDynamicInstrumentation( } } + public static void stopDynamicInstrumentation() { + if (!dynamicInstrumentationEnabled.compareAndSet(true, false)) { + return; + } + LOGGER.info("Stopping Dynamic Instrumentation"); + unsubscribeConfigurationPoller(); + if (configurationUpdater != null) { + // uninstall all probes by providing empty configuration + configurationUpdater.accept(REMOTE_CONFIG, Collections.emptyList()); + } + if (symDBEnablement != null) { + symDBEnablement.stopSymbolExtraction(); + } + } + + public static void startExceptionReplay() { + if (!exceptionReplayEnabled.compareAndSet(false, true)) { + return; + } + LOGGER.info("Starting Exception Replay"); + Config config = Config.get(); + exceptionDebugger = + new DefaultExceptionDebugger( + configurationUpdater, + classNameFilter, + Duration.ofSeconds(config.getDebuggerExceptionCaptureInterval()), + config.getDebuggerMaxExceptionPerSecond()); + DebuggerContext.initExceptionDebugger(exceptionDebugger); + } + + public static void stopExceptionReplay() { + if (!exceptionReplayEnabled.compareAndSet(true, false)) { + return; + } + LOGGER.info("Stopping Exception Replay"); + if (configurationUpdater != null) { + // uninstall all exception probes by providing empty configuration + configurationUpdater.accept(EXCEPTION, Collections.emptyList()); + } + exceptionDebugger = null; + DebuggerContext.initExceptionDebugger(null); + } + + public static void startCodeOriginForSpans() { + if (!codeOriginEnabled.compareAndSet(false, true)) { + return; + } + LOGGER.info("Starting Code Origin for spans"); + DebuggerContext.initCodeOrigin( + new DefaultCodeOriginRecorder(Config.get(), configurationUpdater)); + } + + public static void stopCodeOriginForSpans() { + if (!codeOriginEnabled.compareAndSet(true, false)) { + return; + } + LOGGER.info("Stopping Code Origin for spans"); + if (configurationUpdater != null) { + // uninstall all code origin probes by providing empty configuration + configurationUpdater.accept(CODE_ORIGIN, Collections.emptyList()); + } + DebuggerContext.initCodeOrigin(null); + } + + public static void startDistributedDebugger() { + if (!distributedDebuggerEnabled.compareAndSet(false, true)) { + return; + } + LOGGER.info("Starting Distributed Debugger"); + } + + public static void stopDistributedDebugger() { + if (!distributedDebuggerEnabled.compareAndSet(true, false)) { + return; + } + LOGGER.info("Sopping Distributed Debugger"); + } + private static DebuggerSink createDebuggerSink(Config config, ProbeStatusSink probeStatusSink) { String tags = getDefaultTagsMergedWithGlobalTags(config); SnapshotSink snapshotSink = @@ -256,6 +328,13 @@ private static void subscribeConfigurationPoller( } } + private static void unsubscribeConfigurationPoller() { + if (configurationPoller != null) { + configurationPoller.removeListeners(Product.LIVE_DEBUGGING); + configurationPoller.removeListeners(Product.LIVE_DEBUGGING_SYMBOL_DB); + } + } + static ClassFileTransformer setupInstrumentTheWorldTransformer( Config config, Instrumentation instrumentation, @@ -342,45 +421,30 @@ public void run() { } } - static class DebuggerReporter implements TracerFlare.Reporter { - - private final ConfigurationUpdater configurationUpdater; - private final DebuggerSink sink; - private final ExceptionProbeManager exceptionProbeManager; - - public DebuggerReporter( - ConfigurationUpdater configurationUpdater, - DebuggerSink sink, - ExceptionProbeManager exceptionProbeManager) { - this.configurationUpdater = configurationUpdater; - this.sink = sink; - this.exceptionProbeManager = exceptionProbeManager; - } - - @Override - public void addReportToFlare(ZipOutputStream zip) throws IOException { - String content = - String.join( - System.lineSeparator(), - "Snapshot url: ", - sink.getSnapshotSink().getUrl().toString(), - "Diagnostic url: ", - sink.getProbeStatusSink().getUrl().toString(), - "SymbolDB url: ", - sink.getSymbolSink().getUrl().toString(), - "Probe definitions:", - configurationUpdater.getAppliedDefinitions().toString(), - "Instrumented probes:", - configurationUpdater.getInstrumentationResults().toString(), - "Probe statuses:", - sink.getProbeStatusSink().getProbeStatuses().toString(), - "SymbolDB stats:", - sink.getSymbolSink().getStats().toString(), - "Exception Fingerprints:", - exceptionProbeManager != null - ? exceptionProbeManager.getFingerprints().toString() - : "N/A"); - TracerFlare.addText(zip, "dynamic_instrumentation.txt", content); - } + private static void addReportToFlare(ZipOutputStream zip) throws IOException { + ExceptionProbeManager exceptionProbeManager = + exceptionDebugger != null ? exceptionDebugger.getExceptionProbeManager() : null; + String content = + String.join( + System.lineSeparator(), + "Snapshot url: ", + sink.getSnapshotSink().getUrl().toString(), + "Diagnostic url: ", + sink.getProbeStatusSink().getUrl().toString(), + "SymbolDB url: ", + sink.getSymbolSink().getUrl().toString(), + "Probe definitions:", + configurationUpdater.getAppliedDefinitions().toString(), + "Instrumented probes:", + configurationUpdater.getInstrumentationResults().toString(), + "Probe statuses:", + sink.getProbeStatusSink().getProbeStatuses().toString(), + "SymbolDB stats:", + sink.getSymbolSink().getStats().toString(), + "Exception Fingerprints:", + exceptionProbeManager != null + ? exceptionProbeManager.getFingerprints().toString() + : "N/A"); + TracerFlare.addText(zip, "dynamic_instrumentation.txt", content); } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultProductConfigUpdater.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultProductConfigUpdater.java new file mode 100644 index 00000000000..3b1597de69f --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DefaultProductConfigUpdater.java @@ -0,0 +1,73 @@ +package com.datadog.debugger.agent; + +import datadog.trace.api.Config; +import datadog.trace.api.config.DebuggerConfig; +import datadog.trace.api.config.TraceInstrumentationConfig; +import datadog.trace.bootstrap.debugger.DebuggerContext; + +class DefaultProductConfigUpdater implements DebuggerContext.ProductConfigUpdater { + + @Override + public void updateConfig( + Boolean dynamicInstrumentationEnabled, + Boolean exceptionReplayEnabled, + Boolean codeOriginEnabled, + Boolean liveDebuggingEnabled) { + startOrStopFeature( + DebuggerConfig.DYNAMIC_INSTRUMENTATION_ENABLED, + dynamicInstrumentationEnabled, + DebuggerAgent::startDynamicInstrumentation, + DebuggerAgent::stopDynamicInstrumentation); + startOrStopFeature( + DebuggerConfig.EXCEPTION_REPLAY_ENABLED, + exceptionReplayEnabled, + DebuggerAgent::startExceptionReplay, + DebuggerAgent::stopExceptionReplay); + startOrStopFeature( + TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED, + codeOriginEnabled, + DebuggerAgent::startCodeOriginForSpans, + DebuggerAgent::stopCodeOriginForSpans); + startOrStopFeature( + DebuggerConfig.DISTRIBUTED_DEBUGGER_ENABLED, + liveDebuggingEnabled, + DebuggerAgent::startDistributedDebugger, + DebuggerAgent::stopDistributedDebugger); + } + + @Override + public boolean isDynamicInstrumentationEnabled() { + return DebuggerAgent.dynamicInstrumentationEnabled.get(); + } + + @Override + public boolean isExceptionReplayEnabled() { + return DebuggerAgent.exceptionReplayEnabled.get(); + } + + @Override + public boolean isCodeOriginEnabled() { + return DebuggerAgent.codeOriginEnabled.get(); + } + + @Override + public boolean isDistributedDebuggerEnabled() { + return DebuggerAgent.distributedDebuggerEnabled.get(); + } + + private static boolean isExplicitlyDisabled(String booleanKey) { + return Config.get().configProvider().isSet(booleanKey) + && !Config.get().configProvider().getBoolean(booleanKey); + } + + private static void startOrStopFeature( + String booleanKey, Boolean currentStatus, Runnable start, Runnable stop) { + if (!isExplicitlyDisabled(booleanKey) && currentStatus != null) { + if (currentStatus) { + start.run(); + } else { + stop.run(); + } + } + } +} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java index 12b35740d05..b0a5be8c96d 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java @@ -84,7 +84,10 @@ private static SymDbRemoteConfigRecord deserializeSymDb(byte[] content) throws I public void stopSymbolExtraction() { LOGGER.debug("Stopping symbol extraction."); - instrumentation.removeTransformer(symbolExtractionTransformer); + if (symbolExtractionTransformer != null) { + instrumentation.removeTransformer(symbolExtractionTransformer); + symbolExtractionTransformer = null; + } } long getLastUploadTimestamp() { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultProductConfigUpdaterTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultProductConfigUpdaterTest.java new file mode 100644 index 00000000000..6f7e7716a01 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DefaultProductConfigUpdaterTest.java @@ -0,0 +1,24 @@ +package com.datadog.debugger.agent; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class DefaultProductConfigUpdaterTest { + + @Test + public void enableDisable() { + DefaultProductConfigUpdater productConfigUpdater = new DefaultProductConfigUpdater(); + productConfigUpdater.updateConfig(null, null, null, null); + productConfigUpdater.updateConfig(true, true, true, true); + assertTrue(productConfigUpdater.isDynamicInstrumentationEnabled()); + assertTrue(productConfigUpdater.isExceptionReplayEnabled()); + assertTrue(productConfigUpdater.isCodeOriginEnabled()); + assertTrue(productConfigUpdater.isDistributedDebuggerEnabled()); + productConfigUpdater.updateConfig(false, false, false, false); + assertFalse(productConfigUpdater.isDynamicInstrumentationEnabled()); + assertFalse(productConfigUpdater.isExceptionReplayEnabled()); + assertFalse(productConfigUpdater.isCodeOriginEnabled()); + assertFalse(productConfigUpdater.isDistributedDebuggerEnabled()); + } +} diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java index b5b63f463e5..d4013f3ebc9 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java @@ -17,6 +17,7 @@ import com.datadog.debugger.agent.ClassesToRetransformFinder; import com.datadog.debugger.agent.Configuration; import com.datadog.debugger.agent.ConfigurationUpdater; +import com.datadog.debugger.agent.DebuggerAgent; import com.datadog.debugger.agent.DebuggerAgentHelper; import com.datadog.debugger.agent.DebuggerTransformer; import com.datadog.debugger.agent.JsonSnapshotSerializer; @@ -56,6 +57,7 @@ import org.joor.Reflect; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIf; @@ -82,6 +84,11 @@ public class ExceptionProbeInstrumentationTest { private MockSampler probeSampler; private MockSampler globalSampler; + @BeforeAll + public static void beforeAll() { + setFieldInConfig(Config.get(), "agentUrl", "http://localhost:8126"); + } + @BeforeEach public void before() { CoreTracer tracer = CoreTracer.builder().build(); @@ -92,6 +99,7 @@ public void before() { ProbeRateLimiter.setSamplerSupplier(rate -> rate < 101 ? probeSampler : globalSampler); ProbeRateLimiter.setGlobalSnapshotRate(1000); // to activate the call to DebuggerContext.handleException + DebuggerAgent.startExceptionReplay(); setFieldInConfig(Config.get(), "debuggerExceptionEnabled", true); setFieldInConfig(Config.get(), "dynamicInstrumentationClassFileDumpEnabled", true); } diff --git a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java index 821ef155a03..e17753545ea 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java +++ b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java @@ -97,6 +97,17 @@ protected void waitForReTransformation(String className) { } } + protected void waitForSpecificLine(String line) { + System.out.println("waitForSpecificLine..."); + try { + lastMatchedLine = + TestApplicationHelper.waitForSpecificLine(LOG_FILENAME, line, lastMatchedLine); + System.out.println("line found!"); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + protected void execute(String methodName, String arg) { Consumer method = methodsByName.get(methodName); if (method == null) { @@ -254,6 +265,12 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio app.waitForReTransformation(className); break; } + case "/app/waitForSpecificLine": + { + String feature = request.getRequestUrl().queryParameter("line"); + app.waitForSpecificLine(feature); + break; + } case "/app/execute": { String methodName = request.getRequestUrl().queryParameter("methodname"); diff --git a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/TestApplicationHelper.java b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/TestApplicationHelper.java index b19aa2175c3..b69e697b704 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/TestApplicationHelper.java +++ b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/TestApplicationHelper.java @@ -90,6 +90,17 @@ public static String waitForReTransformation( Duration.ofSeconds(TIMEOUT_S)); } + public static String waitForSpecificLine(String logFileName, String specificLine, String fromLine) + throws IOException { + return waitForSpecificLogLine( + Paths.get(logFileName), + fromLine != null ? line -> line.contains(fromLine) : null, + line -> line.contains(specificLine), + () -> {}, + Duration.ofMillis(SLEEP_MS), + Duration.ofSeconds(TIMEOUT_S)); + } + public static void waitForUpload(String logFileName, int expectedUploads) throws IOException { if (expectedUploads == -1) { System.out.println("wait for " + TIMEOUT_S + "s"); diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java index d366c671f48..053c331c482 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java @@ -14,7 +14,9 @@ import com.datadog.debugger.sink.Snapshot; import com.datadog.debugger.util.MoshiHelper; import com.datadog.debugger.util.MoshiSnapshotTestHelper; +import com.squareup.moshi.Json; import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; import com.squareup.moshi.Types; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.ProbeRateLimiter; @@ -80,7 +82,10 @@ public abstract class BaseIntegrationTest { protected static final MockResponse EMPTY_200_RESPONSE = new MockResponse().setResponseCode(200); private static final ByteString DIAGNOSTICS_STR = ByteString.encodeUtf8("diagnostics"); - private static final String CONFIG_ID = UUID.randomUUID().toString(); + private static final String LD_CONFIG_ID = UUID.randomUUID().toString(); + private static final String APM_CONFIG_ID = UUID.randomUUID().toString(); + public static final String LIVE_DEBUGGING_PRODUCT = "LIVE_DEBUGGING"; + public static final String APM_TRACING_PRODUCT = "APM_TRACING"; protected MockWebServer datadogAgentServer; private MockDispatcher probeMockDispatcher; @@ -90,6 +95,7 @@ public abstract class BaseIntegrationTest { protected Path logFilePath; protected Process targetProcess; private Configuration currentConfiguration; + private ConfigOverrides configOverrides; private boolean configProvided; protected final Object configLock = new Object(); protected final List> intakeRequestListeners = @@ -147,6 +153,7 @@ protected List getDebuggerCommandParams() { "-Ddd.profiling.enabled=false", "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=info", "-Ddatadog.slf4j.simpleLogger.log.com.datadog.debugger=debug", + "-Ddatadog.slf4j.simpleLogger.log.datadog.remoteconfig=debug", "-Ddd.jmxfetch.start-delay=0", "-Ddd.jmxfetch.enabled=false", "-Ddd.dynamic.instrumentation.enabled=true", @@ -413,10 +420,12 @@ private MockResponse datadogAgentDispatch(RecordedRequest request) { return EMPTY_200_RESPONSE; } - private MockResponse handleConfigRequests() { + protected MockResponse handleConfigRequests() { Configuration configuration; + ConfigOverrides configOverrides; synchronized (configLock) { configuration = getCurrentConfiguration(); + configOverrides = getConfigOverrides(); configProvided = true; configLock.notifyAll(); } @@ -426,9 +435,22 @@ private MockResponse handleConfigRequests() { try { JsonAdapter adapter = MoshiConfigTestHelper.createMoshiConfig().adapter(Configuration.class); - String json = adapter.toJson(configuration); - LOG.info("Sending json config: {}", json); - String remoteConfigJson = RemoteConfigHelper.encode(json, CONFIG_ID); + String liveDebuggingJson = adapter.toJson(configuration); + LOG.info("Sending Live Debugging json: {}", liveDebuggingJson); + List remoteConfigs = new ArrayList<>(); + remoteConfigs.add( + new RemoteConfigHelper.RemoteConfig( + LIVE_DEBUGGING_PRODUCT, liveDebuggingJson, LD_CONFIG_ID)); + if (configOverrides != null) { + JsonAdapter configAdapter = + new Moshi.Builder().build().adapter(ConfigOverrides.class); + String configOverridesJson = configAdapter.toJson(configOverrides); + LOG.info("Sending configOverrides json: {}", configOverridesJson); + remoteConfigs.add( + new RemoteConfigHelper.RemoteConfig( + APM_TRACING_PRODUCT, configOverridesJson, APM_CONFIG_ID)); + } + String remoteConfigJson = RemoteConfigHelper.encode(remoteConfigs); return new MockResponse().setResponseCode(200).setBody(remoteConfigJson); } catch (Exception e) { throw new RuntimeException(e); @@ -441,6 +463,12 @@ private Configuration getCurrentConfiguration() { } } + private ConfigOverrides getConfigOverrides() { + synchronized (configLock) { + return configOverrides; + } + } + protected boolean isConfigProvided() { synchronized (configLock) { return configProvided; @@ -461,6 +489,12 @@ protected void setCurrentConfiguration(Configuration configuration) { } } + protected void setConfigOverrides(ConfigOverrides configOverrides) { + synchronized (configLock) { + this.configOverrides = configOverrides; + } + } + protected Configuration createConfig(LogProbe logProbe) { return createConfig(Arrays.asList(logProbe)); } @@ -614,4 +648,23 @@ int getPort() { return socket.getLocalPort(); } } + + static final class ConfigOverrides { + @Json(name = "lib_config") + public LibConfig libConfig; + } + + static final class LibConfig { + @Json(name = "dynamic_instrumentation_enabled") + public Boolean dynamicInstrumentationEnabled; + + @Json(name = "exception_replay_enabled") + public Boolean exceptionReplayEnabled; + + @Json(name = "code_origin_enabled") + public Boolean codeOriginEnabled; + + @Json(name = "live_debugging_enabled") + public Boolean liveDebuggingEnabled; + } } diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java index 9c22dd34643..1d91d1a2694 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java @@ -50,7 +50,7 @@ void testCodeOriginTraceAnnotation() throws Exception { assertEquals("runTracedMethod", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_METHOD)); assertEquals( "(java.lang.String)", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_SIGNATURE)); - assertEquals("134", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_LINE)); + assertEquals("145", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_LINE)); codeOrigin.set(true); } } 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 new file mode 100644 index 00000000000..8a7c7606df8 --- /dev/null +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/InProductEnablementIntegrationTest.java @@ -0,0 +1,77 @@ +package datadog.smoketest; + +import com.datadog.debugger.probe.LogProbe; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class InProductEnablementIntegrationTest extends ServerAppDebuggerIntegrationTest { + private List additionalJvmArgs = new ArrayList<>(); + + @Override + protected ProcessBuilder createProcessBuilder(Path logFilePath, String... params) { + List commandParams = getDebuggerCommandParams(); + // remove the dynamic instrumentation flag + commandParams.remove("-Ddd.dynamic.instrumentation.enabled=true"); + commandParams.addAll(additionalJvmArgs); + return ProcessBuilderHelper.createProcessBuilder( + commandParams, logFilePath, getAppClass(), params); + } + + @Test + @DisplayName("testDynamicInstrumentationEnablement") + void testDynamicInstrumentationEnablement() throws Exception { + appUrl = startAppAndAndGetUrl(); + setConfigOverrides(createConfigOverrides(true, false)); + LogProbe probe = + LogProbe.builder().probeId(PROBE_ID).where(TEST_APP_CLASS_NAME, TRACED_METHOD_NAME).build(); + setCurrentConfiguration(createConfig(probe)); + waitForFeatureStarted(appUrl, "Dynamic Instrumentation"); + waitForInstrumentation(appUrl); + // disable DI + setConfigOverrides(createConfigOverrides(false, false)); + waitForFeatureStopped(appUrl, "Dynamic Instrumentation"); + waitForReTransformation(appUrl); // wait for retransformation of removed probe + } + + @Test + @DisplayName("testExceptionReplayEnablement") + void testExceptionReplayEnablement() throws Exception { + additionalJvmArgs.add("-Ddd.third.party.excludes=datadog.smoketest"); + appUrl = startAppAndAndGetUrl(); + setConfigOverrides(createConfigOverrides(false, true)); + waitForFeatureStarted(appUrl, "Exception Replay"); + execute(appUrl, TRACED_METHOD_NAME, "oops"); // instrumenting first exception + waitForInstrumentation(appUrl); + // disable ER + setConfigOverrides(createConfigOverrides(false, false)); + waitForFeatureStopped(appUrl, "Exception Replay"); + waitForReTransformation(appUrl); // wait for retransformation of removed probes + } + + private void waitForFeatureStarted(String appUrl, String feature) throws IOException { + String line = "INFO com.datadog.debugger.agent.DebuggerAgent - Starting " + feature; + String url = String.format(appUrl + "/waitForSpecificLine?line=%s", line); + sendRequest(url); + LOG.info("feature {} started", feature); + } + + private void waitForFeatureStopped(String appUrl, String feature) throws IOException { + String line = "INFO com.datadog.debugger.agent.DebuggerAgent - Stopping " + feature; + String url = String.format(appUrl + "/waitForSpecificLine?line=%s", line); + sendRequest(url); + LOG.info("feature {} stopped", feature); + } + + private static ConfigOverrides createConfigOverrides( + boolean dynamicInstrumentationEnabled, boolean exceptionReplayEnabled) { + ConfigOverrides config = new ConfigOverrides(); + config.libConfig = new LibConfig(); + config.libConfig.dynamicInstrumentationEnabled = dynamicInstrumentationEnabled; + config.libConfig.exceptionReplayEnabled = exceptionReplayEnabled; + return config; + } +} diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/RemoteConfigHelper.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/RemoteConfigHelper.java index 17fbcd73a30..d9f7c7cc85d 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/RemoteConfigHelper.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/RemoteConfigHelper.java @@ -2,50 +2,107 @@ import datadog.trace.util.Strings; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Base64; +import java.util.List; +import java.util.StringJoiner; public class RemoteConfigHelper { - public static String encode(String config, String configId) { - String hashStr = null; - try { - hashStr = Strings.sha256(config); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); + public static class RemoteConfig { + public final String product; + public final String config; + public final String configId; + + public RemoteConfig(String product, String config, String configId) { + this.product = product; + this.config = config; + this.configId = configId; } - String targetsStr = - String.format( - "{\"signed\":\n" - + " { \"_type\":\"targets\",\n" - + " \"spec_version\": \"1.0\",\n" - + " \"version\": \"2\",\n" - + " \"custom\": { \"opaque_backend_state\": \"opaque\" },\n" - + " \"targets\":\n" - + " { \"datadog/2/LIVE_DEBUGGING/%s/config\":{" - + " \"length\": %d,\n" - + " \"custom\": { \"v\": 123 },\n" - + " \"hashes\":\n" - + " {\n" - + " \"sha256\": \"%s\"\n" - + " }" - + " }" - + " }" - + " }" - + "}", - configId, config.length(), hashStr); - String targetsEncoding = new String(Base64.getEncoder().encode(targetsStr.getBytes())); - String encodedConfig = new String(Base64.getEncoder().encode(config.getBytes())); + } + + public static String encode(List remoteConfigs) { + List hashes = buildHashes(remoteConfigs); + String targetsStr = buildTargets(hashes, remoteConfigs); + String targetsEncoded = new String(Base64.getEncoder().encode(targetsStr.getBytes())); + String targetFiles = buildTargetFiles(remoteConfigs); + String clientConfigs = buildClientConfigs(remoteConfigs); return String.format( "{\n" + "\"targets\": \"%s\",\n" + "\"target_files\": [\n" - + " {\n" - + " \"path\": \"datadog/2/LIVE_DEBUGGING/%s/config\",\n" - + " \"raw\": \"%s\"\n" - + "}]," + + " %s\n" + + "]," + "\"client_configs\": [\n" - + " \"datadog/2/LIVE_DEBUGGING/%s/config\"\n" + + " %s\n" + " ]" + "}", - targetsEncoding, configId, encodedConfig, configId); + targetsEncoded, targetFiles, clientConfigs); + } + + private static String buildClientConfigs(List remoteConfigs) { + StringJoiner sj = new StringJoiner(",\n"); + for (RemoteConfig rc : remoteConfigs) { + sj.add( + String.format( + " \"datadog/2/%s/%s/config\"\n", rc.product, rc.configId)); + } + return sj.toString(); + } + + private static String buildTargetFiles(List remoteConfigs) { + StringJoiner sj = new StringJoiner(",\n"); + for (RemoteConfig rc : remoteConfigs) { + String encodedConfig = new String(Base64.getEncoder().encode(rc.config.getBytes())); + sj.add( + String.format( + " {\n" + + " \"path\": \"datadog/2/%s/%s/config\",\n" + + " \"raw\": \"%s\"\n" + + " }\n", + rc.product, rc.configId, encodedConfig)); + } + return sj.toString(); + } + + private static List buildHashes(List remoteConfigs) { + List hashes = new ArrayList<>(); + for (RemoteConfig rc : remoteConfigs) { + try { + hashes.add(Strings.sha256(rc.config)); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + return hashes; + } + + private static String buildTargets(List hashes, List remoteConfigs) { + StringJoiner sj = new StringJoiner(",\n"); + for (int i = 0; i < remoteConfigs.size(); i++) { + RemoteConfig rc = remoteConfigs.get(i); + sj.add( + String.format( + " \"datadog/2/%s/%s/config\":{" + + " \"length\": %d,\n" + + " \"custom\": { \"v\": 123 },\n" + + " \"hashes\":\n" + + " {\n" + + " \"sha256\": \"%s\"\n" + + " }" + + " }", + rc.product, rc.configId, rc.config.length(), hashes.get(i))); + } + String targets = sj.toString(); + return String.format( + "{\"signed\":\n" + + " { \"_type\":\"targets\",\n" + + " \"spec_version\": \"1.0\",\n" + + " \"version\": \"2\",\n" + + " \"custom\": { \"opaque_backend_state\": \"opaque\" },\n" + + " \"targets\":\n" + + " { %s }" + + " }" + + "}", + targets); } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java b/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java index 0b576654936..27ec36027ce 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java @@ -356,15 +356,15 @@ public DDSpan addThrowable(Throwable error, byte errorPriority) { setTag(DDTags.ERROR_MSG, message); setTag(DDTags.ERROR_TYPE, error.getClass().getName()); - if (isExceptionDebuggingEnabled()) { + if (isExceptionReplayEnabled()) { DebuggerContext.handleException(error, this); } } return this; } - private boolean isExceptionDebuggingEnabled() { - if (!Config.get().isDebuggerExceptionEnabled()) { + private boolean isExceptionReplayEnabled() { + if (!DebuggerContext.isExceptionReplayEnabled()) { return false; } boolean captureOnlyRootSpan = diff --git a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java index bf5421617e7..60bb3a4a3bc 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java @@ -4,6 +4,10 @@ import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_HTTP_HEADER_TAGS; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_LOGS_INJECTION; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_DATA_STREAMS_ENABLED; +import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_ENABLE_CODE_ORIGIN; +import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION; +import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_ENABLE_EXCEPTION_REPLAY; +import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RATE; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RULES; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_TRACING_ENABLED; @@ -24,6 +28,7 @@ import datadog.trace.api.Config; import datadog.trace.api.DynamicConfig; import datadog.trace.api.sampling.SamplingRule; +import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.logging.GlobalLogLevelSwitcher; import datadog.trace.logging.LogLevel; import java.io.ByteArrayInputStream; @@ -65,7 +70,11 @@ public void start(Config config, SharedCommunicationObjects sco) { | CAPABILITY_APM_HTTP_HEADER_TAGS | CAPABILITY_APM_CUSTOM_TAGS | CAPABILITY_APM_TRACING_DATA_STREAMS_ENABLED - | CAPABILITY_APM_TRACING_SAMPLE_RULES); + | CAPABILITY_APM_TRACING_SAMPLE_RULES + | CAPABILITY_APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION + | CAPABILITY_APM_TRACING_ENABLE_EXCEPTION_REPLAY + | CAPABILITY_APM_TRACING_ENABLE_CODE_ORIGIN + | CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING); } stopPolling = new Updater().register(config, configPoller); } @@ -211,7 +220,11 @@ void applyConfigOverrides(LibConfig libConfig) { maybeOverride(builder::setTraceSampleRate, libConfig.traceSampleRate); maybeOverride(builder::setTracingTags, parseTagListToMap(libConfig.tracingTags)); - + DebuggerContext.updateConfig( + libConfig.dynamicInstrumentationEnabled, + libConfig.exceptionReplayEnabled, + libConfig.codeOriginEnabled, + libConfig.liveDebuggingEnabled); builder.apply(); } @@ -282,6 +295,18 @@ static final class LibConfig { @Json(name = "tracing_sampling_rules") public TracingSamplingRules tracingSamplingRules; + + @Json(name = "dynamic_instrumentation_enabled") + public Boolean dynamicInstrumentationEnabled; + + @Json(name = "exception_replay_enabled") + public Boolean exceptionReplayEnabled; + + @Json(name = "code_origin_enabled") + public Boolean codeOriginEnabled; + + @Json(name = "live_debugging_enabled") + public Boolean liveDebuggingEnabled; } /** Holds the raw JSON string and the parsed rule data. */ diff --git a/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java b/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java index 5aea9ca50bd..b8e1124a1b2 100644 --- a/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java +++ b/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java @@ -38,4 +38,8 @@ public interface Capabilities { long CAPABILITY_ASM_NETWORK_FINGERPRINT = 1L << 34; long CAPABILITY_ASM_HEADER_FINGERPRINT = 1L << 35; long CAPABILITY_ASM_RASP_CMDI = 1L << 37; + long CAPABILITY_APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION = 1L << 38; + long CAPABILITY_APM_TRACING_ENABLE_EXCEPTION_REPLAY = 1L << 39; + long CAPABILITY_APM_TRACING_ENABLE_CODE_ORIGIN = 1L << 40; + long CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING = 1L << 41; }