Skip to content

Commit 9604295

Browse files
author
duke
committed
Backport 681dab7205190176b842bd42914b1cb9fe752e44
1 parent 89c5659 commit 9604295

File tree

4 files changed

+145
-5
lines changed

4 files changed

+145
-5
lines changed

src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public final class PlatformEventType extends Type {
4949
private final boolean isJDK;
5050
private final boolean isMethodSampling;
5151
private final boolean isCPUTimeMethodSampling;
52+
private final boolean isBackToBackSensitive;
5253
private final List<SettingDescriptor> settings = new ArrayList<>(5);
5354
private final boolean dynamicSettings;
5455
private final int stackTraceOffset;
@@ -82,10 +83,25 @@ public final class PlatformEventType extends Type {
8283
this.isJVM = Type.isDefinedByJVM(id);
8384
this.isMethodSampling = determineMethodSampling();
8485
this.isCPUTimeMethodSampling = isJVM && name.equals(Type.EVENT_NAME_PREFIX + "CPUTimeSample");
86+
this.isBackToBackSensitive = determineBackToBackSensitive();
8587
this.isJDK = isJDK;
8688
this.stackTraceOffset = determineStackTraceOffset();
8789
}
8890

91+
private boolean determineBackToBackSensitive() {
92+
if (getName().equals(Type.EVENT_NAME_PREFIX + "ThreadDump")) {
93+
return true;
94+
}
95+
if (getName().equals(Type.EVENT_NAME_PREFIX + "ClassLoaderStatistics")) {
96+
return true;
97+
}
98+
return false;
99+
}
100+
101+
public boolean isBackToBackSensitive() {
102+
return isBackToBackSensitive;
103+
}
104+
89105
private boolean isExceptionEvent() {
90106
switch (getName()) {
91107
case Type.EVENT_NAME_PREFIX + "JavaErrorThrow" :

src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ synchronized long start(PlatformRecording recording) {
263263
if (toDisk) {
264264
PeriodicEvents.setFlushInterval(streamInterval);
265265
}
266-
PeriodicEvents.doChunkBegin();
266+
PeriodicEvents.doChunkBegin(true);
267267
Duration duration = recording.getDuration();
268268
if (duration != null) {
269269
recording.setStopTime(startTime.plus(duration));
@@ -335,7 +335,7 @@ synchronized void stop(PlatformRecording recording) {
335335
finishChunk(currentChunk, stopTime, null);
336336
}
337337
currentChunk = newChunk;
338-
PeriodicEvents.doChunkBegin();
338+
PeriodicEvents.doChunkBegin(false);
339339
}
340340

341341
if (toDisk) {
@@ -390,7 +390,7 @@ synchronized void rotateDisk() {
390390
finishChunk(currentChunk, timestamp, null);
391391
}
392392
currentChunk = newChunk;
393-
PeriodicEvents.doChunkBegin();
393+
PeriodicEvents.doChunkBegin(false);
394394
}
395395

396396
private List<PlatformRecording> getRunningRecordings() {

src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/PeriodicEvents.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,14 @@ public static boolean removeEvent(Runnable runnable) {
6969
return taskRepository.removeTask(runnable);
7070
}
7171

72-
public static void doChunkBegin() {
72+
public static void doChunkBegin(boolean startRecording) {
7373
long timestamp = JVM.counterTime();
7474
for (EventTask task : taskRepository.getTasks()) {
7575
var eventType = task.getEventType();
7676
if (eventType.isEnabled() && eventType.isBeginChunk()) {
77-
task.run(timestamp, PeriodicType.BEGIN_CHUNK);
77+
if (!eventType.isBackToBackSensitive() || startRecording) {
78+
task.run(timestamp, PeriodicType.BEGIN_CHUNK);
79+
}
7880
}
7981
}
8082
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package jdk.jfr.event.runtime;
24+
25+
import java.io.IOException;
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.time.Instant;
29+
import java.util.Collections;
30+
import java.util.LinkedHashSet;
31+
import java.util.Set;
32+
33+
import jdk.jfr.Configuration;
34+
import jdk.jfr.Event;
35+
import jdk.jfr.Recording;
36+
import jdk.jfr.StackTrace;
37+
import jdk.jfr.consumer.RecordedClassLoader;
38+
import jdk.jfr.consumer.RecordingStream;
39+
40+
/**
41+
* @test
42+
* @summary The test verifies that jdk.ClassLoaderStatistics and
43+
* jdk.ThreadThreadDump are not emitted at the beginning of a chunk
44+
* when the period is everyChunk, as is the case in default.jfc
45+
* @requires vm.flagless
46+
* @requires vm.hasJFR
47+
* @library /test/lib /test/jdk
48+
* @run main/othervm jdk.jfr.event.runtime.TestBackToBackSensitive
49+
*/
50+
public class TestBackToBackSensitive {
51+
@StackTrace(false)
52+
static class FillEvent extends Event {
53+
String message;
54+
}
55+
56+
public static void main(String... arg) throws Exception {
57+
Set<Instant> threadDumps = Collections.synchronizedSet(new LinkedHashSet<>());
58+
Set<Instant> classLoaderStatistics = Collections.synchronizedSet(new LinkedHashSet<>());
59+
Set<Instant> physicalMemory = Collections.synchronizedSet(new LinkedHashSet<>());
60+
61+
Configuration configuration = Configuration.getConfiguration("default");
62+
try (RecordingStream r1 = new RecordingStream(configuration)) {
63+
r1.setMaxSize(Long.MAX_VALUE);
64+
r1.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime()));
65+
r1.onEvent("jdk.ClassLoaderStatistics", e -> {
66+
RecordedClassLoader cl = e.getValue("classLoader");
67+
if (cl != null) {
68+
if (cl.getType().getName().contains("PlatformClassLoader")) {
69+
classLoaderStatistics.add(e.getStartTime());
70+
}
71+
}
72+
});
73+
r1.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime()));
74+
// Start chunk 1
75+
r1.startAsync();
76+
try (Recording r2 = new Recording()) {
77+
// Start chunk 2
78+
r2.start();
79+
// Starts chunk 3
80+
r2.stop();
81+
}
82+
// Start chunk 4 by filling up chunk 3
83+
for (int i = 0; i < 1_500_000; i++) {
84+
FillEvent f = new FillEvent();
85+
f.commit();
86+
}
87+
r1.stop();
88+
long chunkFiles = filesInRepository();
89+
System.out.println("Number of chunk files: " + chunkFiles);
90+
// When jdk.ClassLoaderStatistics and jdk.ThreadThreadDump are expected to be
91+
// emitted:
92+
// Chunk 1: begin, end
93+
// Chunk 2: begin, end
94+
// Chunk 3: end
95+
// Chunk 4: end
96+
assertCount("jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2));
97+
assertCount("jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2));
98+
// When jdk.PhysicalMemory is expected to be emitted:
99+
// Chunk 1: begin, end
100+
// Chunk 2: begin, end
101+
// Chunk 3: begin, end
102+
// Chunk 4: begin, end
103+
assertCount("jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles);
104+
}
105+
}
106+
107+
private static long filesInRepository() throws IOException {
108+
Path repository = Path.of(System.getProperty("jdk.jfr.repository"));
109+
return Files.list(repository).filter(p -> p.toString().endsWith(".jfr")).count();
110+
}
111+
112+
private static void assertCount(String eventName, Set<Instant> timestamps, long expected) throws Exception {
113+
System.out.println("Timestamps for " + eventName + ":");
114+
for (Instant timestamp : timestamps) {
115+
System.out.println(timestamp);
116+
}
117+
int count = timestamps.size();
118+
if (count != expected) {
119+
throw new Exception("Expected " + expected + " timestamps for event " + eventName + ", but got " + count);
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)