Skip to content
Permalink
Browse files
7156: [JFR Writer] Allow providing extra recording settings
Reviewed-by: hirt
  • Loading branch information
Jaroslav Bachorik authored and thegreystone committed Mar 16, 2021
1 parent 20d8777 commit 21118208a19a025df27d3c82442abe80f87f3e59
@@ -50,8 +50,10 @@

import org.openjdk.jmc.flightrecorder.writer.api.Annotation;
import org.openjdk.jmc.flightrecorder.writer.api.Recording;
import org.openjdk.jmc.flightrecorder.writer.api.RecordingSettings;
import org.openjdk.jmc.flightrecorder.writer.api.TypeStructureBuilder;
import org.openjdk.jmc.flightrecorder.writer.api.TypedValue;
import org.openjdk.jmc.flightrecorder.writer.api.Types;

/**
* The main entry point to JFR recording functionality. Allows to define custom types and initiate
@@ -95,12 +97,13 @@

private final ConstantPools constantPools = new ConstantPools();
private final MetadataImpl metadata = new MetadataImpl(constantPools);
private final TypesImpl types = new TypesImpl(metadata);
private final TypesImpl types;

public RecordingImpl(OutputStream output) {
this.startTicks = System.nanoTime();
this.startNanos = System.currentTimeMillis() * 1_000_000L;
public RecordingImpl(OutputStream output, RecordingSettings settings) {
this.startTicks = settings.getStartTimestamp();
this.startNanos = settings.getStartTimestamp();
this.outputStream = output;
this.types = new TypesImpl(metadata, settings.shouldInitializeJDKTypes());
writeFileHeader();

chunkDataMergingService.submit(() -> {
@@ -131,7 +134,6 @@ private void processChunkDataQueue(long pollTimeout, TimeUnit timeUnit) throws I

@Override
public RecordingImpl rotateChunk() {
System.err.println("=== rotate chunk");
Chunk chunk = getChunk();
activeChunks.remove(chunk);
threadChunk.remove();
@@ -211,7 +213,7 @@ public TypeImpl registerEventType(String name, Consumer<TypeStructureBuilder> bu
return registerType(name, "jdk.jfr.Event", builder -> {
builder.addField("stackTrace", TypesImpl.JDK.STACK_TRACE).addField("eventThread", TypesImpl.JDK.THREAD)
.addField("startTime", TypesImpl.Builtin.LONG,
field -> field.addAnnotation(TypesImpl.JDK.ANNOTATION_TIMESTAMP, "TICKS"));
field -> field.addAnnotation(Types.JDK.ANNOTATION_TIMESTAMP, "TICKS"));
builderCallback.accept(builder);
});
}
@@ -269,9 +271,10 @@ private void writeFileHeader() {
globalWriter.writeBytes(MAGIC).writeShortRaw(MAJOR_VERSION).writeShortRaw(MINOR_VERSION).writeLongRaw(0L) // size placeholder
.writeLongRaw(0L) // CP event offset
.writeLongRaw(0L) // meta event offset
.writeLongRaw(startNanos) // start timestamp
.writeLongRaw(startNanos) // start time in nanoseconds
.writeLongRaw(0L) // duration placeholder
.writeLongRaw(startTicks).writeLongRaw(1_000_000_000L) // 1 tick = 1 ns
.writeLongRaw(startTicks) // start time in ticks
.writeLongRaw(1_000_000_000L) // 1 tick = 1 ns
.writeIntRaw(1); // use compressed integers
}

@@ -0,0 +1,27 @@
package org.openjdk.jmc.flightrecorder.writer;

import org.openjdk.jmc.flightrecorder.writer.api.RecordingSettings;
import org.openjdk.jmc.flightrecorder.writer.api.RecordingSettingsBuilder;

public final class RecordingSettingsBuilderImpl implements RecordingSettingsBuilder {
private long timestamp = -1;
private boolean initializeJdkTypes = false;

@Override
public RecordingSettingsBuilder withTimestamp(long timestamp) {
this.timestamp = timestamp;
return this;
}

@Override
public RecordingSettingsBuilder withJdkTypeInitialization() {
initializeJdkTypes = true;
return this;
}

@Override
public RecordingSettings build() {
return new RecordingSettings(timestamp > 0 ? timestamp : System.currentTimeMillis() * 1_000_000L,
initializeJdkTypes);
}
}
@@ -49,12 +49,18 @@
private final MetadataImpl metadata;

TypesImpl(MetadataImpl metadata) {
this(metadata, true);
}

TypesImpl(MetadataImpl metadata, boolean initialize) {
metadata.setTypes(this);
this.metadata = metadata;

registerBuiltins();
registerJdkTypes();
this.metadata.resolveTypes(); // resolve all back-referenced types
if (initialize) {
registerJdkTypes();
this.metadata.resolveTypes(); // resolve all back-referenced types
}
}

private void registerBuiltins() {
@@ -0,0 +1,66 @@
package org.openjdk.jmc.flightrecorder.writer.api;

/**
* A settings data-class for a {@linkplain Recording} instance
*/
public final class RecordingSettings {
private final long startTimestamp;
private final boolean initializeJDKTypes;

/**
* @param startTimestamp
* the recording start timestamp in epoch nanoseconds (nanoseconds since 1970-01-01)
* @param initializeJDKTypes
* should the {@linkplain org.openjdk.jmc.flightrecorder.writer.api.Types.JDK} types
* be initialized
*/
public RecordingSettings(long startTimestamp, boolean initializeJDKTypes) {
this.startTimestamp = startTimestamp;
this.initializeJDKTypes = initializeJDKTypes;
}

/**
* Recording will use current time as its start timestamp
*
* @param initializeJDKTypes
* should the {@linkplain org.openjdk.jmc.flightrecorder.writer.api.Types.JDK} types
* be initialized
*/
public RecordingSettings(boolean initializeJDKTypes) {
this(-1, initializeJDKTypes);
}

/**
* Recording will initialize {@linkplain org.openjdk.jmc.flightrecorder.writer.api.Types.JDK}
* types.
*
* @param startTimestamp
* the recording start timestamp in epoch nanoseconds (nanoseconds since 1970-01-01)
*/
public RecordingSettings(long startTimestamp) {
this(startTimestamp, true);
}

/**
* Recording will use current time as its start timestamp and will initialize
* {@linkplain org.openjdk.jmc.flightrecorder.writer.api.Types.JDK} types.
*/
public RecordingSettings() {
this(-1, true);
}

/**
* @return recording timestamp in epoch nanoseconds (nanoseconds since 1970-01-01)
*/
public long getStartTimestamp() {
return startTimestamp;
}

/**
* @return {@literal true} if {@linkplain org.openjdk.jmc.flightrecorder.writer.api.Types.JDK}
* types are to be initialized
*/
public boolean shouldInitializeJDKTypes() {
return initializeJDKTypes;
}
}
@@ -0,0 +1,30 @@
package org.openjdk.jmc.flightrecorder.writer.api;

/**
* A builder type for {@linkplain RecordingSettings}
*/
public interface RecordingSettingsBuilder {
/**
* Set the recording timestamp in epoch nanoseconds (nanoseconds since 1970-01-01).
*
* @param timestamp
* the timestamp in epoch nanoseconds (nanoseconds since 1970-01-01)
* @return this instance for chaining
*/
RecordingSettingsBuilder withTimestamp(long timestamp);

/**
* The recording will automatically initialize
* {@linkplain org.openjdk.jmc.flightrecorder.writer.api.Types.JDK} types.
*
* @return this instance for chaining
*/
RecordingSettingsBuilder withJdkTypeInitialization();

/**
* Build the settings instance.
*
* @return the settings instance
*/
RecordingSettings build();
}
@@ -34,6 +34,7 @@
package org.openjdk.jmc.flightrecorder.writer.api;

import org.openjdk.jmc.flightrecorder.writer.RecordingImpl;
import org.openjdk.jmc.flightrecorder.writer.RecordingSettingsBuilderImpl;

import java.io.BufferedOutputStream;
import java.io.File;
@@ -42,24 +43,127 @@
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;

/**
* A factory class to create new {@linkplain Recording} instances
*/
public final class Recordings {
/**
* Create a new recording that will be automatically stored in the given file. The recording
* start timestamp is set to the current time and the recording will automatically initialize
* JDK types.
*
* @param path
* the path to the recording file
* @return a new {@linkplain Recording} instance
* @throws IOException
*/
public static Recording newRecording(String path) throws IOException {
return newRecording(Paths.get(path));
}

/**
* Create a new recording that will be automatically stored in the given file.
*
* @param path
* the path to the recording file
* @param settingsCallback
* settings callback
* @return a new {@linkplain Recording} instance
* @throws IOException
*/
public static Recording newRecording(String path, Consumer<RecordingSettingsBuilder> settingsCallback)
throws IOException {
return newRecording(Paths.get(path), settingsCallback);
}

/**
* Create a new recording that will be automatically stored in the given path. The recording
* start timestamp is set to the current time and the recording will automatically initialize
* JDK types.
*
* @param path
* the path to the recording file
* @return a new {@linkplain Recording} instance
* @throws IOException
*/
public static Recording newRecording(Path path) throws IOException {
return newRecording(path.toFile());
}

/**
* Create a new recording that will be automatically stored in the given path.
*
* @param path
* the path to the recording file
* @param settingsCallback
* settings callback
* @return a new {@linkplain Recording} instance
* @throws IOException
*/
public static Recording newRecording(Path path, Consumer<RecordingSettingsBuilder> settingsCallback)
throws IOException {
return newRecording(path.toFile(), settingsCallback);
}

/**
* Create a new recording that will be automatically stored in the given file. The recording
* start timestamp is set to the current time and the recording will automatically initialize
* JDK types.
*
* @param path
* the path to the recording file
* @return a new {@linkplain Recording} instance
* @throws IOException
*/
public static Recording newRecording(File path) throws IOException {
return new RecordingImpl(new FileOutputStream(path));
return newRecording(new FileOutputStream(path));
}

/**
* Create a new recording that will be automatically stored in the given file.
*
* @param path
* the path to the recording file
* @param settingsCallback
* settings callback
* @return a new {@linkplain Recording} instance
* @throws IOException
*/
public static Recording newRecording(File path, Consumer<RecordingSettingsBuilder> settingsCallback)
throws IOException {
return newRecording(new FileOutputStream(path), settingsCallback);
}

/**
* Create a new recording that will be automatically stored in the given stream. The recording
* start timestamp is set to the current time and the recording will automatically initialize
* JDK types.
*
* @param recordingStream
* recording output stream
* @return a new {@linkplain Recording} instance
*/
public static Recording newRecording(OutputStream recordingStream) {
return new RecordingImpl(new BufferedOutputStream(recordingStream));
return newRecording(recordingStream, RecordingSettingsBuilder::withJdkTypeInitialization);
}

/**
* Create a new recording that will be automatically stored in the given stream.
*
* @param recordingStream
* recording output stream
* @param settingsCallback
* settings callback
* @return a new {@linkplain Recording} instance
*/
public static Recording newRecording(
OutputStream recordingStream, Consumer<RecordingSettingsBuilder> settingsCallback) {
RecordingSettingsBuilderImpl builder = new RecordingSettingsBuilderImpl();
if (settingsCallback != null) {
settingsCallback.accept(builder);
}
return new RecordingImpl(new BufferedOutputStream(recordingStream), builder.build());
}
}
@@ -47,6 +47,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.openjdk.jmc.flightrecorder.writer.api.Annotation;
import org.openjdk.jmc.flightrecorder.writer.api.RecordingSettings;
import org.openjdk.jmc.flightrecorder.writer.api.Types;

class RecordingImplTest {
@@ -56,7 +57,7 @@
@BeforeEach
void setUp() {
bos = new ByteArrayOutputStream();
recording = new RecordingImpl(bos);
recording = new RecordingImpl(bos, new RecordingSettings());
}

@AfterEach

0 comments on commit 2111820

Please sign in to comment.