mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8365972: JFR: ThreadDump and ClassLoaderStatistics events may cause back to back rotations
Reviewed-by: mgronlun
This commit is contained in:
parent
49f51f9450
commit
681dab7205
@ -48,6 +48,7 @@ public final class PlatformEventType extends Type {
|
||||
private final boolean isJDK;
|
||||
private final boolean isMethodSampling;
|
||||
private final boolean isCPUTimeMethodSampling;
|
||||
private final boolean isBackToBackSensitive;
|
||||
private final List<SettingDescriptor> settings = new ArrayList<>(5);
|
||||
private final boolean dynamicSettings;
|
||||
private final int stackTraceOffset;
|
||||
@ -81,10 +82,25 @@ public final class PlatformEventType extends Type {
|
||||
this.isJVM = Type.isDefinedByJVM(id);
|
||||
this.isMethodSampling = determineMethodSampling();
|
||||
this.isCPUTimeMethodSampling = isJVM && name.equals(Type.EVENT_NAME_PREFIX + "CPUTimeSample");
|
||||
this.isBackToBackSensitive = determineBackToBackSensitive();
|
||||
this.isJDK = isJDK;
|
||||
this.stackTraceOffset = determineStackTraceOffset();
|
||||
}
|
||||
|
||||
private boolean determineBackToBackSensitive() {
|
||||
if (getName().equals(Type.EVENT_NAME_PREFIX + "ThreadDump")) {
|
||||
return true;
|
||||
}
|
||||
if (getName().equals(Type.EVENT_NAME_PREFIX + "ClassLoaderStatistics")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isBackToBackSensitive() {
|
||||
return isBackToBackSensitive;
|
||||
}
|
||||
|
||||
private boolean isExceptionEvent() {
|
||||
switch (getName()) {
|
||||
case Type.EVENT_NAME_PREFIX + "JavaErrorThrow" :
|
||||
|
||||
@ -263,7 +263,7 @@ public final class PlatformRecorder {
|
||||
if (toDisk) {
|
||||
PeriodicEvents.setFlushInterval(streamInterval);
|
||||
}
|
||||
PeriodicEvents.doChunkBegin();
|
||||
PeriodicEvents.doChunkBegin(true);
|
||||
Duration duration = recording.getDuration();
|
||||
if (duration != null) {
|
||||
recording.setStopTime(startTime.plus(duration));
|
||||
@ -335,7 +335,7 @@ public final class PlatformRecorder {
|
||||
finishChunk(currentChunk, stopTime, null);
|
||||
}
|
||||
currentChunk = newChunk;
|
||||
PeriodicEvents.doChunkBegin();
|
||||
PeriodicEvents.doChunkBegin(false);
|
||||
}
|
||||
|
||||
if (toDisk) {
|
||||
@ -390,7 +390,7 @@ public final class PlatformRecorder {
|
||||
finishChunk(currentChunk, timestamp, null);
|
||||
}
|
||||
currentChunk = newChunk;
|
||||
PeriodicEvents.doChunkBegin();
|
||||
PeriodicEvents.doChunkBegin(false);
|
||||
}
|
||||
|
||||
private List<PlatformRecording> getRunningRecordings() {
|
||||
|
||||
@ -69,12 +69,14 @@ public final class PeriodicEvents {
|
||||
return taskRepository.removeTask(runnable);
|
||||
}
|
||||
|
||||
public static void doChunkBegin() {
|
||||
public static void doChunkBegin(boolean startRecording) {
|
||||
long timestamp = JVM.counterTime();
|
||||
for (EventTask task : taskRepository.getTasks()) {
|
||||
var eventType = task.getEventType();
|
||||
if (eventType.isEnabled() && eventType.isBeginChunk()) {
|
||||
task.run(timestamp, PeriodicType.BEGIN_CHUNK);
|
||||
if (!eventType.isBackToBackSensitive() || startRecording) {
|
||||
task.run(timestamp, PeriodicType.BEGIN_CHUNK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
122
test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java
Normal file
122
test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jfr.event.runtime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Configuration;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.StackTrace;
|
||||
import jdk.jfr.consumer.RecordedClassLoader;
|
||||
import jdk.jfr.consumer.RecordingStream;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary The test verifies that jdk.ClassLoaderStatistics and
|
||||
* jdk.ThreadThreadDump are not emitted at the beginning of a chunk
|
||||
* when the period is everyChunk, as is the case in default.jfc
|
||||
* @requires vm.flagless
|
||||
* @requires vm.hasJFR
|
||||
* @library /test/lib /test/jdk
|
||||
* @run main/othervm jdk.jfr.event.runtime.TestBackToBackSensitive
|
||||
*/
|
||||
public class TestBackToBackSensitive {
|
||||
@StackTrace(false)
|
||||
static class FillEvent extends Event {
|
||||
String message;
|
||||
}
|
||||
|
||||
public static void main(String... arg) throws Exception {
|
||||
Set<Instant> threadDumps = Collections.synchronizedSet(new LinkedHashSet<>());
|
||||
Set<Instant> classLoaderStatistics = Collections.synchronizedSet(new LinkedHashSet<>());
|
||||
Set<Instant> physicalMemory = Collections.synchronizedSet(new LinkedHashSet<>());
|
||||
|
||||
Configuration configuration = Configuration.getConfiguration("default");
|
||||
try (RecordingStream r1 = new RecordingStream(configuration)) {
|
||||
r1.setMaxSize(Long.MAX_VALUE);
|
||||
r1.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime()));
|
||||
r1.onEvent("jdk.ClassLoaderStatistics", e -> {
|
||||
RecordedClassLoader cl = e.getValue("classLoader");
|
||||
if (cl != null) {
|
||||
if (cl.getType().getName().contains("PlatformClassLoader")) {
|
||||
classLoaderStatistics.add(e.getStartTime());
|
||||
}
|
||||
}
|
||||
});
|
||||
r1.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime()));
|
||||
// Start chunk 1
|
||||
r1.startAsync();
|
||||
try (Recording r2 = new Recording()) {
|
||||
// Start chunk 2
|
||||
r2.start();
|
||||
// Starts chunk 3
|
||||
r2.stop();
|
||||
}
|
||||
// Start chunk 4 by filling up chunk 3
|
||||
for (int i = 0; i < 1_500_000; i++) {
|
||||
FillEvent f = new FillEvent();
|
||||
f.commit();
|
||||
}
|
||||
r1.stop();
|
||||
long chunkFiles = filesInRepository();
|
||||
System.out.println("Number of chunk files: " + chunkFiles);
|
||||
// When jdk.ClassLoaderStatistics and jdk.ThreadThreadDump are expected to be
|
||||
// emitted:
|
||||
// Chunk 1: begin, end
|
||||
// Chunk 2: begin, end
|
||||
// Chunk 3: end
|
||||
// Chunk 4: end
|
||||
assertCount("jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2));
|
||||
assertCount("jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2));
|
||||
// When jdk.PhysicalMemory is expected to be emitted:
|
||||
// Chunk 1: begin, end
|
||||
// Chunk 2: begin, end
|
||||
// Chunk 3: begin, end
|
||||
// Chunk 4: begin, end
|
||||
assertCount("jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles);
|
||||
}
|
||||
}
|
||||
|
||||
private static long filesInRepository() throws IOException {
|
||||
Path repository = Path.of(System.getProperty("jdk.jfr.repository"));
|
||||
return Files.list(repository).filter(p -> p.toString().endsWith(".jfr")).count();
|
||||
}
|
||||
|
||||
private static void assertCount(String eventName, Set<Instant> timestamps, long expected) throws Exception {
|
||||
System.out.println("Timestamps for " + eventName + ":");
|
||||
for (Instant timestamp : timestamps) {
|
||||
System.out.println(timestamp);
|
||||
}
|
||||
int count = timestamps.size();
|
||||
if (count != expected) {
|
||||
throw new Exception("Expected " + expected + " timestamps for event " + eventName + ", but got " + count);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user