Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import org.apache.lucene.gradle.plugins.LuceneGradlePlugin;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
Expand Down Expand Up @@ -189,7 +190,7 @@ public void apply(Project project) {
task.jvmArgs(
List.of(
"-XX:StartFlightRecording=dumponexit=true,maxsize=250M,settings="
+ gradlePluginResource(project, "testing/profiling.jfc"),
+ gradlePluginResource(project, profilingSettingsPath()),
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+DebugNonSafepoints"));
task.dependsOn(cleanPreviousProfiles);
Expand Down Expand Up @@ -254,4 +255,15 @@ private record ProfilingOptions(
Provider<Integer> countOption,
Provider<Boolean> lineNumbersOption,
Provider<Boolean> frametypesOption) {}

/**
* Java 25+ JEP 509 ({@code jdk.CPUTimeSample}) is currently implemented only on Linux in the JDK.
* When the Gradle host is Linux, use {@code testing/profiling.cputime.jfc} (CPU-time sampling
* only). Otherwise use {@code testing/profiling.jfc} (legacy wall-clock execution sampling). Host
* selection may broaden if CPU-time sampling is supported on other OS releases later.
*/
private static String profilingSettingsPath() {
String os = System.getProperty("os.name", "").toLowerCase(Locale.ROOT);
return os.contains("linux") ? "testing/profiling.cputime.jfc" : "testing/profiling.jfc";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,16 @@ static boolean isInteresting(String mode, RecordedEvent event) {
String name = event.getEventType().getName();
switch (mode) {
case "cpu":
return (name.equals("jdk.ExecutionSample") || name.equals("jdk.NativeMethodSample"))
&& !isGradlePollThread(event.getThread("sampledThread"));
// jdk.CPUTimeSample: JEP 509 (e.g. gradle/testing/profiling.cputime.jfc). Legacy samples:
// jdk.ExecutionSample / jdk.NativeMethodSample (e.g. profiling.jfc). Stock Lucene configs
// enable one family; filter gradle poll thread for legacy samples only.
if (name.equals("jdk.CPUTimeSample")) {
return true;
}
if (name.equals("jdk.ExecutionSample") || name.equals("jdk.NativeMethodSample")) {
return !isGradlePollThread(event.getThread("sampledThread"));
}
return false;
case "heap":
return (name.equals("jdk.ObjectAllocationInNewTLAB")
|| name.equals("jdk.ObjectAllocationOutsideTLAB"))
Expand All @@ -132,6 +140,8 @@ static long getValue(RecordedEvent event) {
return 1L;
case "jdk.NativeMethodSample":
return 1L;
case "jdk.CPUTimeSample":
return 1L;
default:
throw new UnsupportedOperationException(event.toString());
}
Expand Down Expand Up @@ -173,6 +183,7 @@ public static void printReport(
if (count < 1) {
throw new IllegalArgumentException("tests.profile.count must be positive");
}

Map<String, SimpleEntry<String, Long>> histogram = new HashMap<>();
int totalEvents = 0;
long sumValues = 0;
Expand Down
39 changes: 39 additions & 0 deletions gradle/testing/profiling.cputime.jfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
CPU-time method sampling (Java 25+, JEP 509, jdk.CPUTimeSample). Used when CPU-time profiling
is available for the test JVM (currently only on Linux for this JDK).

Object allocation events match profiling.jfc for heap profiling mode.
-->
<configuration version="2.0" label="TestProfilingCpuTime" description="CPU-time sampling for tests (JEP 509)" provider="Apache">
<event name="jdk.CPUTimeSample">
<setting name="enabled">true</setting>
<setting name="throttle">1 ms</setting>
</event>

<event name="jdk.ObjectAllocationInNewTLAB">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>

<event name="jdk.ObjectAllocationOutsideTLAB">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
</configuration>
4 changes: 3 additions & 1 deletion gradle/testing/profiling.jfc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
limitations under the License.
-->
<!--
Collects only execution and method samples at a low interval
Wall-clock execution samples for CPU profiling when JEP 509 CPU-time sampling is not used for
tests (see profiling.cputime.jfc on hosts where CodeProfilingPlugin selects it).
Heap allocation events are enabled on all platforms.
-->
<configuration version="2.0" label="TestProfiling" description="Sampling for unit tests" provider="Apache">
<event name="jdk.ExecutionSample">
Expand Down
5 changes: 5 additions & 0 deletions lucene/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ Changes in Runtime Behavior

Build
---------------------
* GITHUB#15926: Use the Java 25+ (JEP 509) CPU-time sampling profiler when available (currently
only on Linux), and fall back to legacy wall-clock execution sampling elsewhere. ProfileResults
recognizes jdk.CPUTimeSample; CodeProfilingPlugin picks gradle/testing/profiling.cputime.jfc or
profiling.jfc by host OS. (Prithvi S)

* GITHUB#15327: New low-level build options to detect abuse of LuceneTestCase.random():
tests.random.maxacquires and tests.random.maxcalls (Robert Muir, Dawid Weiss)

Expand Down
Loading