Summary
Add methods RecordingStream::stop() and RemoteRecordingStream::stop() to finish a recording stream without closing it.
Problem
A recording stream can be created to monitor what happens during some piece of code, for example, a test method like this:
AtomicBoolean socketUse = new AtomicBoolean();
try (var r = new RecordingStream()) {
r.setMaxSize(Long.MAX_VALUE);
r.enable("jdk.SocketWrite").withoutThreshold();
r.enable("jdk.SocketRead").withoutThreshold();
r.onEvent(event -> socketUse.set(true));
r.startAsync();
testFoo();
// ???? Wait for events here
if (socketUse.get()) {
r.dump(Path.of("socket-events.jfr"));
throw new AssertionError("testFoo() should not use network");
}
}
The problem is that there is no good way to ensure all events have been consumed in the stream before checking the result.
This limitation has led users to abandon the RecordingStream and instead use a combination of RecordingFile and Recording class to manage the life cycle. It works, but the code becomes more complex and they can't leverage other benefits provided by the RecordingStream class, such as filtering and sorting of events.
Solution
Introduce a stop() method that will finish the recording, but wait for all events to be consumed before returning.
r.startAsync();
testFoo();
r.stop();
if (socketUse.get()) {
r.dump(Path.of("socket-events.jfr"));
throw new AssertionError("testFoo() should not use network");
}
The jdk.jfr.consumer.Recording and jdk.management.jfr.FlightRecorderMXBean classes already have stop() methods, so extending the functionality to RecordingStream and RemoteRecordingStream is a natural fit.
Specification
jdk.jfr.RecordingStream:
+ /**
+ * Stops the recording stream.
+ * <p>
+ * Stops a started stream and waits until all events in the recording have
+ * been consumed.
+ * <p>
+ * Invoking this method in an action, for example in the
+ * {@link #onEvent(Consumer)} method, could block the stream indefinitely.
+ * To stop the stream abruptly, use the {@link #close} method.
+ * <p>
+ * The following code snippet illustrates how this method can be used in
+ * conjunction with the {@link #startAsync()} method to monitor what happens
+ * during a test method:
+ * <p>
+ * {@snippet class="Snippets" region="RecordingStreamStop"}
+ *
+ * @return {@code true} if recording is stopped, {@code false} otherwise
+ *
+ * @throws IllegalStateException if the recording is not started or is already stopped
+ */
jdk.managment.jfr.RemoteRecordingStream:
+ /**
+ * Stops the recording stream.
+ * <p>
+ * Stops a started stream and waits until all events in the recording have
+ * been consumed.
+ * <p>
+ * Invoking this method in an action, for example in the
+ * {@link #onEvent(Consumer)} method, could block the stream indefinitely.
+ * To stop the stream abruptly, use the {@link #close} method.
+ * <p>
+ * The following code snippet illustrates how this method can be used in
+ * conjunction with the {@link #startAsync()} method to monitor what happens
+ * during a test method:
+ * <p>
+ * {@snippet :
+ * AtomicLong bytesWritten = new AtomicLong();
+ * try (var r = new RemoteRecordingStream(connection)) {
+ * r.setMaxSize(Long.MAX_VALUE);
+ * r.enable("jdk.FileWrite").withoutThreshold();
+ * r.onEvent(event ->
+ * bytesWritten.addAndGet(event.getLong("bytesWritten"))
+ * );
+ * r.startAsync();
+ * testFoo();
+ * r.stop();
+ * if (bytesWritten.get() > 1_000_000L) {
+ * r.dump(Path.of("file-write-events.jfr"));
+ * throw new AssertionError("testFoo() write to much data to disk");
+ * }
+ * }
+ * }
+ * @return {@code true} if recording is stopped, {@code false} otherwise
+ *
+ * @throws IllegalStateException if the recording is not started or is already stopped
+ */
+ public boolean stop() {
- csr of
-
JDK-8295350 JFR: Add stop methods for recording streams
-
- Resolved
-