8335779: JFR: Hide sleep events

Reviewed-by: mgronlun
This commit is contained in:
Erik Gahlin 2024-07-10 14:28:20 +00:00
parent 537d20afbf
commit e0fb949460
14 changed files with 196 additions and 104 deletions

View File

@ -47,7 +47,7 @@ class JfrIntrinsicSupport : AllStatic {
#define JFR_HAVE_INTRINSICS
#define JFR_TEMPLATES(template) \
template(jdk_jfr_internal_HiddenWait, "jdk/jfr/internal/HiddenWait") \
template(jdk_jfr_internal_management_HiddenWait, "jdk/jfr/internal/management/HiddenWait") \
template(jdk_jfr_internal_JVM, "jdk/jfr/internal/JVM") \
template(jdk_jfr_internal_event_EventWriterFactory, "jdk/jfr/internal/event/EventWriterFactory") \
template(jdk_jfr_internal_event_EventConfiguration_signature, "Ljdk/jfr/internal/event/EventConfiguration;") \

View File

@ -1442,7 +1442,7 @@ bool ObjectMonitor::check_owner(TRAPS) {
static inline bool is_excluded(const Klass* monitor_klass) {
assert(monitor_klass != nullptr, "invariant");
NOT_JFR_RETURN_(false);
JFR_ONLY(return vmSymbols::jdk_jfr_internal_HiddenWait() == monitor_klass->name();)
JFR_ONLY(return vmSymbols::jdk_jfr_internal_management_HiddenWait() == monitor_klass->name();)
}
static void post_monitor_wait_event(EventJavaMonitorWait* event,

View File

@ -31,6 +31,7 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.jfr.Event;
import jdk.jfr.internal.event.EventConfiguration;
import jdk.jfr.internal.event.EventWriter;
import jdk.jfr.internal.management.HiddenWait;
/**
* Interface against the JVM.

View File

@ -30,6 +30,7 @@ import java.time.LocalDateTime;
import jdk.jfr.Recording;
import jdk.jfr.internal.event.EventConfiguration;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.util.ValueFormatter;
@ -118,7 +119,8 @@ public final class JVMSupport {
lastTimestamp = time;
return;
}
Utils.takeNap(1);
HiddenWait hiddenWait = new HiddenWait();
hiddenWait.takeNap(1);
}
}

View File

@ -47,6 +47,7 @@ import jdk.jfr.Period;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.consumer.RepositoryFiles;
import jdk.jfr.internal.event.EventConfiguration;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.periodic.PeriodicEvents;
import jdk.jfr.internal.util.Utils;
@ -57,6 +58,7 @@ public final class MetadataRepository {
private final Map<String, EventType> nativeEventTypes = LinkedHashMap.newHashMap(150);
private final Map<String, EventControl> nativeControls = LinkedHashMap.newHashMap(150);
private final SettingsManager settingsManager = new SettingsManager();
private final HiddenWait threadSleeper = new HiddenWait();
private Constructor<EventConfiguration> cachedEventConfigurationConstructor;
private boolean staleMetadata = true;
private boolean unregistered;
@ -341,7 +343,7 @@ public final class MetadataRepository {
lastMillis = millis;
return;
}
Utils.takeNap(1);
threadSleeper.takeNap(1);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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
@ -33,6 +33,7 @@ import jdk.jfr.RecordingState;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.management.EventByteStream;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.management.ManagementSupport;
public final class OngoingStream extends EventByteStream {
@ -44,6 +45,7 @@ public final class OngoingStream extends EventByteStream {
private final RepositoryFiles repositoryFiles;
private final Recording recording;
private final HiddenWait threadSleeper = new HiddenWait();
private final int blockSize;
private final long endTimeNanos;
private final byte[] headerBytes = new byte[HEADER_SIZE];
@ -195,19 +197,13 @@ public final class OngoingStream extends EventByteStream {
return bytes;
}
}
takeNap();
if (!threadSleeper.takeNap(10)) {
throw new IOException("Read operation interrupted");
}
}
return EMPTY_ARRAY;
}
private void takeNap() throws IOException {
try {
Thread.sleep(10);
} catch (InterruptedException ie) {
throw new IOException("Read operation interrupted", ie);
}
}
private boolean ensureInput() throws IOException {
if (input == null) {
if (SecuritySupport.getFileSize(new SafePath(path)) < HEADER_SIZE) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2024, 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
@ -31,6 +31,8 @@ import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Path;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.util.Utils;
public final class RecordingInput implements DataInput, AutoCloseable {
@ -67,6 +69,7 @@ public final class RecordingInput implements DataInput, AutoCloseable {
}
private final int blockSize;
private final FileAccess fileAccess;
private final HiddenWait threadSleeper = new HiddenWait();
private long pollCount = 1000;
private RandomAccessFile file;
private String filename;
@ -453,6 +456,6 @@ public final class RecordingInput implements DataInput, AutoCloseable {
if (pollCount < 0) {
throw new IOException("Recording file is stuck in locked stream state.");
}
Utils.takeNap(1);
threadSleeper.takeNap(1);
}
}

View File

@ -46,9 +46,10 @@ import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.management.HiddenWait;;
public final class RepositoryFiles {
private static final Object WAIT_OBJECT = new Object();
private static final HiddenWait WAIT_OBJECT = new HiddenWait();
private static final String DIRECTORY_PATTERN = "DDDD_DD_DD_DD_DD_DD_";
public static void notifyNewFile() {
synchronized (WAIT_OBJECT) {
@ -59,7 +60,7 @@ public final class RepositoryFiles {
private final FileAccess fileAccess;
private final NavigableMap<Long, Path> pathSet = new TreeMap<>();
private final Map<Path, Long> pathLookup = new HashMap<>();
private final Object waitObject;
private final HiddenWait waitObject;
private boolean allowSubDirectory;
private volatile boolean closed;
private Path repository;
@ -67,7 +68,7 @@ public final class RepositoryFiles {
public RepositoryFiles(FileAccess fileAccess, Path repository, boolean allowSubDirectory) {
this.repository = repository;
this.fileAccess = fileAccess;
this.waitObject = repository == null ? WAIT_OBJECT : new Object();
this.waitObject = repository == null ? WAIT_OBJECT : new HiddenWait();
this.allowSubDirectory = allowSubDirectory;
}
@ -108,7 +109,7 @@ public final class RepositoryFiles {
// was accessed. Just ignore, and retry later.
}
if (wait) {
nap();
waitObject.takeNap(1000);
} else {
return pathLookup.size() > beforeSize;
}
@ -157,16 +158,6 @@ public final class RepositoryFiles {
}
}
private void nap() {
try {
synchronized (waitObject) {
waitObject.wait(1000);
}
} catch (InterruptedException e) {
// ignore
}
}
private boolean updatePaths() throws IOException, DirectoryIteratorException {
boolean foundNew = false;
Path repoPath = repository;

View File

@ -22,11 +22,21 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal;
package jdk.jfr.internal.management;
/**
* The HiddenWait class is used to exclude jdk.JavaMonitorWait events
* from being generated when Object.wait() is called on an object of this type.
*/
public final class HiddenWait {
public synchronized boolean takeNap(long timeoutMillis) {
try {
this.wait(timeoutMillis);
return true;
} catch (InterruptedException e) {
// Ok, ignore
return false;
}
}
}

View File

@ -39,45 +39,57 @@ import java.io.IOException;
* processing should not continue.
*/
public final class StreamBarrier implements Closeable {
private final HiddenWait lock = new HiddenWait();
private boolean activated = false;
private boolean used = false;
private long end = Long.MAX_VALUE;
// Blocks thread until barrier is deactivated
public synchronized void check() {
while (activated) {
try {
this.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
public void check() {
synchronized (lock) {
while (activated) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public synchronized void setStreamEnd(long timestamp) {
end = timestamp;
public void setStreamEnd(long timestamp) {
synchronized(lock) {
end = timestamp;
}
}
public synchronized long getStreamEnd() {
return end;
public long getStreamEnd() {
synchronized(lock) {
return end;
}
}
public synchronized void activate() {
activated = true;
used = true;
public void activate() {
synchronized (lock) {
activated = true;
used = true;
}
}
@Override
public synchronized void close() throws IOException {
activated = false;
this.notifyAll();
synchronized (lock) {
activated = false;
lock.notifyAll();
}
}
/**
* Returns {@code true) if barrier is, or has been, in active state, {@code false) otherwise.
*/
public synchronized boolean used() {
return used;
public boolean used() {
synchronized (lock) {
return used;
}
}
}

View File

@ -48,19 +48,19 @@ import jdk.internal.module.Checks;
import jdk.jfr.Event;
import jdk.jfr.EventType;
import jdk.jfr.RecordingState;
import jdk.jfr.internal.HiddenWait;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MirrorEvent;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.settings.PeriodSetting;
import jdk.jfr.internal.settings.StackTraceSetting;
import jdk.jfr.internal.settings.ThresholdSetting;
public final class Utils {
private static final Object flushObject = new Object();
private static final HiddenWait flushObject = new HiddenWait();
private static final String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
/**
@ -351,17 +351,6 @@ public final class Utils {
return Type.isValidJavaFieldType(type.getName());
}
public static void takeNap(long millis) {
HiddenWait hiddenWait = new HiddenWait();
try {
synchronized(hiddenWait) {
hiddenWait.wait(millis);
}
} catch (InterruptedException e) {
// ok
}
}
public static void notifyFlush() {
synchronized (flushObject) {
flushObject.notifyAll();
@ -369,13 +358,7 @@ public final class Utils {
}
public static void waitFlush(long timeOut) {
synchronized (flushObject) {
try {
flushObject.wait(timeOut);
} catch (InterruptedException e) {
// OK
}
}
flushObject.takeNap(timeOut);
}
public static Instant epochNanosToInstant(long epochNanos) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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
@ -30,12 +30,14 @@ import java.util.HashMap;
import java.util.Map;
import jdk.jfr.internal.management.ManagementSupport;
import jdk.jfr.internal.management.HiddenWait;
final class DownLoadThread extends Thread {
private final RemoteRecordingStream stream;
private final Instant startTime;
private final Instant endTime;
private final DiskRepository diskRepository;
private final HiddenWait threadSleeper = new HiddenWait();
DownLoadThread(RemoteRecordingStream stream, String name) {
super(name);
@ -64,7 +66,7 @@ final class DownLoadThread extends Thread {
if (bytes.length != 0) {
diskRepository.write(bytes);
} else {
takeNap();
threadSleeper.takeNap(1000);
}
}
} catch (IOException ioe) {
@ -73,12 +75,4 @@ final class DownLoadThread extends Thread {
diskRepository.complete();
}
}
private void takeNap() {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
// ignore
}
}
}

View File

@ -32,8 +32,10 @@ import java.util.ArrayDeque;
import java.util.Deque;
import jdk.management.jfr.DiskRepository.DiskChunk;
import jdk.jfr.internal.management.HiddenWait;
final class FileDump {
private final HiddenWait lock = new HiddenWait();
private final Deque<DiskChunk> chunks = new ArrayDeque<>();
private final long stopTimeMillis;
private boolean complete;
@ -42,45 +44,53 @@ final class FileDump {
this.stopTimeMillis = stopTimeMillis;
}
public synchronized void add(DiskChunk dc) {
if (isComplete()) {
return;
}
dc.acquire();
chunks.addFirst(dc);
long endMillis = dc.endTimeNanos / 1_000_000;
if (endMillis >= stopTimeMillis) {
setComplete();
public void add(DiskChunk dc) {
synchronized (lock) {
if (isComplete()) {
return;
}
dc.acquire();
chunks.addFirst(dc);
long endMillis = dc.endTimeNanos / 1_000_000;
if (endMillis >= stopTimeMillis) {
setComplete();
}
}
}
public synchronized boolean isComplete() {
return complete;
}
public synchronized void setComplete() {
complete = true;
this.notifyAll();
}
public synchronized void close() {
for (DiskChunk dc : chunks) {
dc.release();
public boolean isComplete() {
synchronized (lock) {
return complete;
}
}
public void setComplete() {
synchronized (lock) {
complete = true;
lock.notifyAll();
}
}
public void close() {
synchronized (lock) {
for (DiskChunk dc : chunks) {
dc.release();
}
chunks.clear();
complete = true;
}
chunks.clear();
complete = true;
}
private DiskChunk oldestChunk() throws InterruptedException {
while (true) {
synchronized (this) {
synchronized (lock) {
if (!chunks.isEmpty()) {
return chunks.pollLast();
}
if (complete) {
return null;
}
this.wait();
lock.wait();
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2024, 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.jvm;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import jdk.jfr.Recording;
import jdk.jfr.Name;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.Events;
/**
* @test TestHiddenWait
* @key jfr
* @summary Checks that JFR code don't emit noise in the form of ThreadSleep and JavaMonitorWait events.
* @requires vm.hasJFR
* @library /test/lib
* @run main/othervm jdk.jfr.jvm.TestHiddenWait
*/
public class TestHiddenWait {
static final String PERIODIC_EVENT_NAME = "test.Periodic";
@Name(PERIODIC_EVENT_NAME)
public static class PeriodicEvent extends Event {
}
public static void main(String... args) throws Exception {
FlightRecorder.addPeriodicEvent(PeriodicEvent.class, () -> {
PeriodicEvent event = new PeriodicEvent();
event.commit();
});
try (Recording r = new Recording()) {
AtomicLong counter = new AtomicLong();
r.enable("jdk.ThreadSleep").withoutThreshold();
r.enable("jdk.JavaMonitorWait").withoutThreshold();
r.enable(PERIODIC_EVENT_NAME).withPeriod(Duration.ofMillis(100));
r.start();
// Triggers Object.wait() in stream barrier
try (RecordingStream b = new RecordingStream()) {
b.startAsync();
b.stop();
}
// Wait for for periodic events
try (RecordingStream s = new RecordingStream()) {
s.onEvent(PERIODIC_EVENT_NAME, e -> {
if (counter.incrementAndGet() >= 2) {
s.close();
}
});
s.start();
}
List<RecordedEvent> events = Events.fromRecording(r);
for (RecordedEvent event : events) {
if (!event.getEventType().getName().equals(PERIODIC_EVENT_NAME)) {
System.out.println(event);
throw new Exception("Didn't expect ThreadSleep or JavaMonitorWait events");
}
}
}
}
}