Skip to content

Commit 8f4fb42

Browse files
authored
Use the built-in CPU profiler on JDK 25 and Linux (#9502)
1 parent 4d88780 commit 8f4fb42

File tree

3 files changed

+53
-19
lines changed

3 files changed

+53
-19
lines changed

dd-java-agent/agent-profiling/profiling-controller-jfr/src/main/resources/jfr/dd.jfp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ jdk.ExecutionSample#enabled=true
4747
# Note: we use 9 ms sampling rate in a hope to avoid 'lockstep' sampling.
4848
# Ideally JFR should provide random jitter in sampling to ensure this doesn't happen.
4949
jdk.ExecutionSample#period=9 ms
50+
# In JDK 25+ we want to use CPUTimeSample instead
51+
# Disabling the ExecutionSample will be done in OpenJDKController impl
52+
jdk.CPUTimeSample#enabled=true
53+
jdk.CPUTimeSample#throttle=9ms
54+
jdk.CPUTimeSamplesLost#enabled=true
55+
# ---
5056
jdk.NativeMethodSample#enabled=false
5157
jdk.SafepointBegin#enabled=true
5258
jdk.SafepointBegin#threshold=0 ms

dd-java-agent/agent-profiling/profiling-controller-openjdk/src/main/java/com/datadog/profiling/controller/openjdk/OpenJdkController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.datadog.profiling.controller.jfr.JfpUtils;
3535
import com.datadog.profiling.controller.openjdk.events.AvailableProcessorCoresEvent;
3636
import datadog.environment.JavaVirtualMachine;
37+
import datadog.environment.OperatingSystem;
3738
import datadog.trace.api.Config;
3839
import datadog.trace.api.config.ProfilingConfig;
3940
import datadog.trace.bootstrap.config.provider.ConfigProvider;
@@ -62,6 +63,8 @@ public final class OpenJdkController implements Controller {
6263
private static final String EXPLICITLY_ENABLED = "explicitly enabled by user";
6364
private static final String EXPENSIVE_ON_CURRENT_JVM =
6465
"expensive on this version of the JVM (" + JavaVirtualMachine.getRuntimeVersion() + ")";
66+
private static final String CPUTIME_SAMPLE_JDK25 = "Switching to CPUTimeSample on JDK 25+";
67+
6568
static final Duration RECORDING_MAX_AGE = Duration.ofMinutes(5);
6669

6770
private final ConfigProvider configProvider;
@@ -164,6 +167,13 @@ public OpenJdkController(final ConfigProvider configProvider)
164167
throw new ConfigurationException(e);
165168
}
166169

170+
// switch to CPUTimeSample event on JDK 25 and Linux
171+
if (JavaVirtualMachine.isJavaVersionAtLeast(25) && OperatingSystem.isLinux()) {
172+
disableEvent(recordingSettings, "jdk.ExecutionSample", CPUTIME_SAMPLE_JDK25);
173+
enableEvent(recordingSettings, "jdk.CPUTimeSample", CPUTIME_SAMPLE_JDK25);
174+
enableEvent(recordingSettings, "jdk.CPUTimeSamplesLost", CPUTIME_SAMPLE_JDK25);
175+
}
176+
167177
// Toggle settings from override args
168178

169179
String disabledEventsArgs = configProvider.getString(ProfilingConfig.PROFILING_DISABLED_EVENTS);

dd-smoke-tests/profiling-integration-tests/src/test/java/datadog/smoketest/JFRBasedProfilingIntegrationTest.java

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import com.fasterxml.jackson.databind.ObjectMapper;
1515
import com.google.common.collect.Multimap;
1616
import datadog.environment.JavaVirtualMachine;
17+
import datadog.environment.OperatingSystem;
18+
import datadog.environment.SystemProperties;
1719
import datadog.trace.api.Pair;
1820
import datadog.trace.api.config.ProfilingConfig;
1921
import delight.fileupload.FileUpload;
@@ -49,8 +51,9 @@
4951
import org.junit.jupiter.api.DisplayName;
5052
import org.junit.jupiter.api.Test;
5153
import org.junit.jupiter.api.TestInfo;
52-
import org.junit.jupiter.api.condition.DisabledIf;
5354
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
55+
import org.junit.jupiter.params.ParameterizedTest;
56+
import org.junit.jupiter.params.provider.ValueSource;
5457
import org.openjdk.jmc.common.IMCStackTrace;
5558
import org.openjdk.jmc.common.item.Aggregators;
5659
import org.openjdk.jmc.common.item.IAttribute;
@@ -68,12 +71,8 @@
6871
import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
6972
import org.slf4j.Logger;
7073
import org.slf4j.LoggerFactory;
71-
import spock.util.environment.OperatingSystem;
7274

7375
@DisabledIfSystemProperty(named = "java.vm.name", matches = ".*J9.*")
74-
@DisabledIf(
75-
value = "isJavaVersionAtLeast24",
76-
disabledReason = "Failing on Java 24. Skip until we have a fix.")
7776
class JFRBasedProfilingIntegrationTest {
7877
private static final Logger log = LoggerFactory.getLogger(JFRBasedProfilingIntegrationTest.class);
7978
private static final Duration ONE_NANO = Duration.ofNanos(1);
@@ -155,47 +154,57 @@ void teardown() throws Exception {
155154
}
156155
}
157156

158-
@Test
157+
@ParameterizedTest
158+
@ValueSource(strings = {"jfr", "ddprof"})
159159
@DisplayName("Test continuous recording - no jmx delay, default compression")
160-
public void testContinuousRecording_no_jmx_delay(final TestInfo testInfo) throws Exception {
160+
public void testContinuousRecording_no_jmx_delay(String profiler, final TestInfo testInfo)
161+
throws Exception {
162+
Assumptions.assumeTrue("jfr".equals(profiler) || OperatingSystem.isLinux());
161163
testWithRetry(
162164
() ->
163165
testContinuousRecording(
164-
0, ENDPOINT_COLLECTION_ENABLED, OperatingSystem.getCurrent().isLinux(), false),
166+
0, ENDPOINT_COLLECTION_ENABLED, "ddprof".equals(profiler), false),
165167
testInfo,
166168
5);
167169
}
168170

169-
@Test
171+
@ParameterizedTest
172+
@ValueSource(strings = {"jfr", "ddprof"})
170173
@DisplayName("Test continuous recording - no jmx delay, zstd compression")
171-
public void testContinuousRecording_no_jmx_delay_jmethodid_cache(final TestInfo testInfo)
174+
public void testContinuousRecording_no_jmx_delay_zstd(String profiler, final TestInfo testInfo)
172175
throws Exception {
176+
Assumptions.assumeTrue("jfr".equals(profiler) || OperatingSystem.isLinux());
173177
testWithRetry(
174178
() ->
175179
testContinuousRecording(
176-
0, ENDPOINT_COLLECTION_ENABLED, OperatingSystem.getCurrent().isLinux(), true),
180+
0, ENDPOINT_COLLECTION_ENABLED, "ddprof".equals(profiler), true),
177181
testInfo,
178182
5);
179183
}
180184

181-
@Test
185+
@ParameterizedTest
186+
@ValueSource(strings = {"jfr", "ddprof"})
182187
@DisplayName("Test continuous recording - 1 sec jmx delay, default compression")
183-
public void testContinuousRecording(final TestInfo testInfo) throws Exception {
188+
public void testContinuousRecording(String profiler, final TestInfo testInfo) throws Exception {
189+
Assumptions.assumeTrue("jfr".equals(profiler) || OperatingSystem.isLinux());
184190
testWithRetry(
185191
() ->
186192
testContinuousRecording(
187-
1, ENDPOINT_COLLECTION_ENABLED, OperatingSystem.getCurrent().isLinux(), false),
193+
1, ENDPOINT_COLLECTION_ENABLED, "ddprof".equals(profiler), false),
188194
testInfo,
189195
5);
190196
}
191197

192-
@Test
198+
@ParameterizedTest
199+
@ValueSource(strings = {"jfr", "ddprof"})
193200
@DisplayName("Test continuous recording - 1 sec jmx delay, zstd compression")
194-
public void testContinuousRecording_zstd(final TestInfo testInfo) throws Exception {
201+
public void testContinuousRecording_zstd(String profiler, final TestInfo testInfo)
202+
throws Exception {
203+
Assumptions.assumeTrue("jfr".equals(profiler) || OperatingSystem.isLinux());
195204
testWithRetry(
196205
() ->
197206
testContinuousRecording(
198-
1, ENDPOINT_COLLECTION_ENABLED, OperatingSystem.getCurrent().isLinux(), true),
207+
1, ENDPOINT_COLLECTION_ENABLED, "ddprof".equals(profiler), true),
199208
testInfo,
200209
5);
201210
}
@@ -368,6 +377,15 @@ private static void verifyJdkEventsDisabled(IItemCollection events) {
368377
assertFalse(events.apply(ItemFilters.type("jdk.ThreadPark")).hasItems());
369378
}
370379

380+
private static void verifyJdkEvents(IItemCollection events) {
381+
String cpuSampleType = "jdk.ExecutionSample";
382+
if (JavaVirtualMachine.isJavaVersionAtLeast(25) && OperatingSystem.isLinux()) {
383+
// for Java 25+ we are defaulting to 'jdk.CPUTimeSample' on Linux
384+
cpuSampleType = "jdk.CPUTimeSample";
385+
}
386+
assertTrue(events.apply(ItemFilters.type(cpuSampleType)).hasItems());
387+
}
388+
371389
private static void verifyDatadogEventsNotCorrupt(IItemCollection events) {
372390
// if we emit any of these events during the test they mustn't have corrupted context
373391
for (String eventName :
@@ -621,6 +639,7 @@ private void assertRecordingEvents(
621639
// TODO ddprof (async) profiler seems to be having some issues with stack depth limit and
622640
// native frames
623641
} else {
642+
verifyJdkEvents(events);
624643
// make sure the stack depth limit is respected
625644
for (IItemIterable lane : events.apply(ItemFilters.type(JdkTypeIDs.EXECUTION_SAMPLE))) {
626645
IMemberAccessor<IMCStackTrace, IItem> stackTraceAccessor =
@@ -838,8 +857,7 @@ private static ProcessBuilder createProcessBuilder(
838857
}
839858

840859
private static String javaPath() {
841-
final String separator = System.getProperty("file.separator");
842-
return System.getProperty("java.home") + separator + "bin" + separator + "java";
860+
return Paths.get(SystemProperties.getOrDefault("java.home", ""), "bin", "java").toString();
843861
}
844862

845863
private static String buildDirectory() {

0 commit comments

Comments
 (0)