From a5f0d365b9fefdb211ddbd4b05b1a4d5b76914d2 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Wed, 7 Jan 2026 10:28:16 +0100 Subject: [PATCH 1/2] Allow running wallclock profiler even without tracer Fix the wallclock context filter logic to allow collecting wallclock profiles even with the tracer being disabled and not providing the tracing context. --- .../ddprof/DatadogProfilerConfig.java | 18 ++ .../JFRBasedProfilingIntegrationTest.java | 163 +++++++++++++----- 2 files changed, 142 insertions(+), 39 deletions(-) diff --git a/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java b/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java index d023d13b219..f9b980f486e 100644 --- a/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java +++ b/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java @@ -142,7 +142,25 @@ public static boolean getWallCollapsing(ConfigProvider configProvider) { PROFILING_DATADOG_PROFILER_WALL_COLLAPSING_DEFAULT); } + /** + * Checks whether the wall-clock context filter should be used.
+ * The context filter will make wall-clock profiler to pick candidate threads only from threads + * with attached tracing context.
+ * If context filter is not used (this method returns {@literal false}) all threads will be + * considered for wallclock sampling. + * + * @param configProvider the associated config provider + * @return {@literal true} if the wallclock sampler should use context filtering, {@literal false} + * otherwise + */ public static boolean getWallContextFilter(ConfigProvider configProvider) { + // Context filtering requires tracing to be enabled - without tracing, + // there are no span contexts to filter on, so threads would never be added + // to the filter, resulting in no walltime samples. + boolean isTracingEnabled = configProvider.getBoolean(TRACE_ENABLED, true); + if (!isTracingEnabled) { + return false; + } return getBoolean( configProvider, PROFILING_DATADOG_PROFILER_WALL_CONTEXT_FILTER, diff --git a/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java b/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java index 5030247339d..deb8e3af834 100644 --- a/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java +++ b/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java @@ -491,6 +491,80 @@ void testBogusApiKey(final TestInfo testInfo) throws Exception { 3); } + @Test + @DisplayName("Test wallclock profiling without tracing") + public void testWallclockProfilingWithoutTracing(final TestInfo testInfo) throws Exception { + Assumptions.assumeTrue(OperatingSystem.isLinux()); + testWithRetry( + () -> { + try { + targetProcess = + createProcessBuilder( + profilingServer.getPort(), + tracingServer.getPort(), + VALID_API_KEY, + 0, + PROFILING_START_DELAY_SECONDS, + PROFILING_UPLOAD_PERIOD_SECONDS, + false, + true, + "on", + 0, + logFilePath, + false) + .start(); + + Assumptions.assumeFalse(JavaVirtualMachine.isJ9()); + + final RecordedRequest request = retrieveRequest(); + assertNotNull(request); + + final List items = + FileUpload.parse( + request.getBody().readByteArray(), request.getHeader("Content-Type")); + + FileItem rawJfr = items.get(1); + assertEquals("main.jfr", rawJfr.getName()); + + assertFalse(logHasErrors(logFilePath)); + InputStream eventStream = new ByteArrayInputStream(rawJfr.get()); + eventStream = decompressStream("on", eventStream); + IItemCollection events = JfrLoaderToolkit.loadEvents(eventStream); + assertTrue(events.hasItems()); + + IItemCollection wallclockSamples = + events.apply(ItemFilters.type("datadog.MethodSample")); + assertTrue( + wallclockSamples.hasItems(), "Expected wallclock samples when tracing is disabled"); + + // Verify span context is not present + for (IItemIterable event : wallclockSamples) { + IMemberAccessor rootSpanIdAccessor = + LOCAL_ROOT_SPAN_ID.getAccessor(event.getType()); + IMemberAccessor spanIdAccessor = + SPAN_ID.getAccessor(event.getType()); + for (IItem sample : event) { + assertEquals( + 0, + rootSpanIdAccessor.getMember(sample).longValue(), + "rootSpanId should be 0 when tracing is disabled"); + assertEquals( + 0, + spanIdAccessor.getMember(sample).longValue(), + "spanId should be 0 when tracing is disabled"); + } + } + } finally { + if (targetProcess != null) { + targetProcess.destroyForcibly(); + } + targetProcess = null; + } + }, + testInfo, + 3); + } + @Test @DisplayName("Test shutdown") @Disabled("https://github.com/DataDog/dd-trace-java/pull/5213") @@ -771,7 +845,8 @@ private ProcessBuilder createProcessBuilder( asyncProfilerEnabled, withCompression, exitDelay, - logFilePath); + logFilePath, + true); } private static ProcessBuilder createProcessBuilder( @@ -785,50 +860,60 @@ private static ProcessBuilder createProcessBuilder( final boolean asyncProfilerEnabled, final String withCompression, final int exitDelay, - final Path logFilePath) { + final Path logFilePath, + final boolean tracingEnabled) { final String templateOverride = JFRBasedProfilingIntegrationTest.class .getClassLoader() .getResource("overrides.jfp") .getFile(); - final List command = - Arrays.asList( - javaPath(), - "-Xmx" + System.getProperty("datadog.forkedMaxHeapSize", "1024M"), - "-Xms" + System.getProperty("datadog.forkedMinHeapSize", "64M"), - "-javaagent:" + agentShadowJar(), - "-XX:ErrorFile=/tmp/hs_err_pid%p.log", - "-Ddd.trace.agent.port=" + tracerPort, - "-Ddd.service.name=smoke-test-java-app", - "-Ddd.env=smoketest", - "-Ddd.version=99", - "-Ddd.profiling.enabled=true", - "-Ddd.profiling.stackdepth=" + STACK_DEPTH_LIMIT, - "-Ddd.profiling.ddprof.enabled=" + asyncProfilerEnabled, - "-Ddd.profiling.ddprof.alloc.enabled=" + asyncProfilerEnabled, - "-Ddd.profiling.agentless=" + (apiKey != null), - "-Ddd.profiling.start-delay=" + profilingStartDelaySecs, - "-Ddd.profiling.upload.period=" + profilingUploadPeriodSecs, - "-Ddd.profiling.url=http://localhost:" + profilerPort, - "-Ddd.profiling.hotspots.enabled=true", - "-Ddd.profiling.endpoint.collection.enabled=" + endpointCollectionEnabled, - "-Ddd.profiling.upload.timeout=" + PROFILING_UPLOAD_TIMEOUT_SECONDS, - "-Ddd.profiling.debug.dump_path=/tmp/dd-profiler", - "-Ddd.profiling.queueing.time.enabled=true", - "-Ddd.profiling.queueing.time.threshold.millis=0", - "-Ddd.profiling.debug.upload.compression=" + withCompression, - "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug", - "-Ddd.profiling.context.attributes=foo,bar", - "-Dorg.slf4j.simpleLogger.defaultLogLevel=debug", - "-XX:+IgnoreUnrecognizedVMOptions", - "-XX:+UnlockCommercialFeatures", - "-XX:+FlightRecorder", - "-Ddd." + ProfilingConfig.PROFILING_TEMPLATE_OVERRIDE_FILE + "=" + templateOverride, - "-Ddd.jmxfetch.start-delay=" + jmxFetchDelaySecs, - "-jar", - profilingShadowJar(), - Integer.toString(exitDelay)); + final List command = new java.util.ArrayList<>(); + command.add(javaPath()); + command.add("-Xmx" + System.getProperty("datadog.forkedMaxHeapSize", "1024M")); + command.add("-Xms" + System.getProperty("datadog.forkedMinHeapSize", "64M")); + command.add("-javaagent:" + agentShadowJar()); + command.add("-XX:ErrorFile=/tmp/hs_err_pid%p.log"); + command.add("-Ddd.trace.agent.port=" + tracerPort); + if (!tracingEnabled) { + command.add("-Ddd.trace.enabled=false"); + } + command.add("-Ddd.service.name=smoke-test-java-app"); + command.add("-Ddd.env=smoketest"); + command.add("-Ddd.version=99"); + command.add("-Ddd.profiling.enabled=true"); + command.add("-Ddd.profiling.stackdepth=" + STACK_DEPTH_LIMIT); + command.add("-Ddd.profiling.ddprof.enabled=" + asyncProfilerEnabled); + command.add("-Ddd.profiling.ddprof.alloc.enabled=" + asyncProfilerEnabled); + if (!tracingEnabled && asyncProfilerEnabled) { + command.add("-Ddd.profiling.ddprof.wall.enabled=true"); + } + command.add("-Ddd.profiling.agentless=" + (apiKey != null)); + command.add("-Ddd.profiling.start-delay=" + profilingStartDelaySecs); + command.add("-Ddd.profiling.upload.period=" + profilingUploadPeriodSecs); + command.add("-Ddd.profiling.url=http://localhost:" + profilerPort); + command.add("-Ddd.profiling.hotspots.enabled=true"); + command.add("-Ddd.profiling.endpoint.collection.enabled=" + endpointCollectionEnabled); + command.add("-Ddd.profiling.upload.timeout=" + PROFILING_UPLOAD_TIMEOUT_SECONDS); + command.add("-Ddd.profiling.debug.dump_path=/tmp/dd-profiler"); + if (tracingEnabled) { + command.add("-Ddd.profiling.queueing.time.enabled=true"); + command.add("-Ddd.profiling.queueing.time.threshold.millis=0"); + command.add("-Ddd.profiling.context.attributes=foo,bar"); + } + command.add("-Ddd.profiling.debug.upload.compression=" + withCompression); + command.add("-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug"); + command.add("-Dorg.slf4j.simpleLogger.defaultLogLevel=debug"); + command.add("-XX:+IgnoreUnrecognizedVMOptions"); + command.add("-XX:+UnlockCommercialFeatures"); + command.add("-XX:+FlightRecorder"); + command.add( + "-Ddd." + ProfilingConfig.PROFILING_TEMPLATE_OVERRIDE_FILE + "=" + templateOverride); + command.add("-Ddd.jmxfetch.start-delay=" + jmxFetchDelaySecs); + command.add("-jar"); + command.add(profilingShadowJar()); + command.add(Integer.toString(exitDelay)); + final ProcessBuilder processBuilder = new ProcessBuilder(command); processBuilder.directory(new File(buildDirectory())); From dde49640845c45d1e2538dc054dec4697e53a156 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Tue, 13 Jan 2026 12:10:33 +0100 Subject: [PATCH 2/2] Simplify changes --- .../JFRBasedProfilingIntegrationTest.java | 84 +++++++++---------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java b/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java index deb8e3af834..65bf000c82e 100644 --- a/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java +++ b/dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java @@ -868,52 +868,44 @@ private static ProcessBuilder createProcessBuilder( .getResource("overrides.jfp") .getFile(); - final List command = new java.util.ArrayList<>(); - command.add(javaPath()); - command.add("-Xmx" + System.getProperty("datadog.forkedMaxHeapSize", "1024M")); - command.add("-Xms" + System.getProperty("datadog.forkedMinHeapSize", "64M")); - command.add("-javaagent:" + agentShadowJar()); - command.add("-XX:ErrorFile=/tmp/hs_err_pid%p.log"); - command.add("-Ddd.trace.agent.port=" + tracerPort); - if (!tracingEnabled) { - command.add("-Ddd.trace.enabled=false"); - } - command.add("-Ddd.service.name=smoke-test-java-app"); - command.add("-Ddd.env=smoketest"); - command.add("-Ddd.version=99"); - command.add("-Ddd.profiling.enabled=true"); - command.add("-Ddd.profiling.stackdepth=" + STACK_DEPTH_LIMIT); - command.add("-Ddd.profiling.ddprof.enabled=" + asyncProfilerEnabled); - command.add("-Ddd.profiling.ddprof.alloc.enabled=" + asyncProfilerEnabled); - if (!tracingEnabled && asyncProfilerEnabled) { - command.add("-Ddd.profiling.ddprof.wall.enabled=true"); - } - command.add("-Ddd.profiling.agentless=" + (apiKey != null)); - command.add("-Ddd.profiling.start-delay=" + profilingStartDelaySecs); - command.add("-Ddd.profiling.upload.period=" + profilingUploadPeriodSecs); - command.add("-Ddd.profiling.url=http://localhost:" + profilerPort); - command.add("-Ddd.profiling.hotspots.enabled=true"); - command.add("-Ddd.profiling.endpoint.collection.enabled=" + endpointCollectionEnabled); - command.add("-Ddd.profiling.upload.timeout=" + PROFILING_UPLOAD_TIMEOUT_SECONDS); - command.add("-Ddd.profiling.debug.dump_path=/tmp/dd-profiler"); - if (tracingEnabled) { - command.add("-Ddd.profiling.queueing.time.enabled=true"); - command.add("-Ddd.profiling.queueing.time.threshold.millis=0"); - command.add("-Ddd.profiling.context.attributes=foo,bar"); - } - command.add("-Ddd.profiling.debug.upload.compression=" + withCompression); - command.add("-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug"); - command.add("-Dorg.slf4j.simpleLogger.defaultLogLevel=debug"); - command.add("-XX:+IgnoreUnrecognizedVMOptions"); - command.add("-XX:+UnlockCommercialFeatures"); - command.add("-XX:+FlightRecorder"); - command.add( - "-Ddd." + ProfilingConfig.PROFILING_TEMPLATE_OVERRIDE_FILE + "=" + templateOverride); - command.add("-Ddd.jmxfetch.start-delay=" + jmxFetchDelaySecs); - command.add("-jar"); - command.add(profilingShadowJar()); - command.add(Integer.toString(exitDelay)); - + final List command = + Arrays.asList( + javaPath(), + "-Xmx" + System.getProperty("datadog.forkedMaxHeapSize", "1024M"), + "-Xms" + System.getProperty("datadog.forkedMinHeapSize", "64M"), + "-javaagent:" + agentShadowJar(), + "-XX:ErrorFile=/tmp/hs_err_pid%p.log", + "-Ddd.trace.agent.port=" + tracerPort, + "-Ddd.service.name=smoke-test-java-app", + "-Ddd.env=smoketest", + "-Ddd.version=99", + "-Ddd.trace.enabled=" + tracingEnabled, + "-Ddd.profiling.enabled=true", + "-Ddd.profiling.stackdepth=" + STACK_DEPTH_LIMIT, + "-Ddd.profiling.ddprof.enabled=" + asyncProfilerEnabled, + "-Ddd.profiling.ddprof.alloc.enabled=" + asyncProfilerEnabled, + "-Ddd.profiling.agentless=" + (apiKey != null), + "-Ddd.profiling.start-delay=" + profilingStartDelaySecs, + "-Ddd.profiling.upload.period=" + profilingUploadPeriodSecs, + "-Ddd.profiling.url=http://localhost:" + profilerPort, + "-Ddd.profiling.hotspots.enabled=true", + "-Ddd.profiling.endpoint.collection.enabled=" + endpointCollectionEnabled, + "-Ddd.profiling.upload.timeout=" + PROFILING_UPLOAD_TIMEOUT_SECONDS, + "-Ddd.profiling.debug.dump_path=/tmp/dd-profiler", + "-Ddd.profiling.queueing.time.enabled=true", + "-Ddd.profiling.queueing.time.threshold.millis=0", + "-Ddd.profiling.debug.upload.compression=" + withCompression, + "-Ddatadog.slf4j.simpleLogger.defaultLogLevel=debug", + "-Ddd.profiling.context.attributes=foo,bar", + "-Dorg.slf4j.simpleLogger.defaultLogLevel=debug", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+UnlockCommercialFeatures", + "-XX:+FlightRecorder", + "-Ddd." + ProfilingConfig.PROFILING_TEMPLATE_OVERRIDE_FILE + "=" + templateOverride, + "-Ddd.jmxfetch.start-delay=" + jmxFetchDelaySecs, + "-jar", + profilingShadowJar(), + Integer.toString(exitDelay)); final ProcessBuilder processBuilder = new ProcessBuilder(command); processBuilder.directory(new File(buildDirectory()));