“9484165923281617181aa8ed88af1e84f0058113”上不存在“...getstarted/git@gitcode.net:s920243400/PaddleDetection.git”
提交 a279f1d6 编写于 作者: D Denghui Dong 提交者: D-D-H

[Backport] 8226511: Implement JFR Event Streaming

Summary:

Test Plan: jdk/jfr

Reviewed-by: yuleil

Issue: https://github.com/alibaba/dragonwell8/issues/112
上级 66bf03b7
...@@ -413,6 +413,36 @@ public final class Recording implements Closeable { ...@@ -413,6 +413,36 @@ public final class Recording implements Closeable {
internal.setMaxSize(maxSize); internal.setMaxSize(maxSize);
} }
/**
* Determines how often events are made available for streaming.
*
* @param interval the interval at which events are made available for streaming.
*
* @throws IllegalArgumentException if {@code interval} is negative
*
* @throws IllegalStateException if the recording is in the {@code CLOSED} state
*
* @since 14
*/
public void setFlushInterval(Duration interval) {
Objects.nonNull(interval);
if (interval.isNegative()) {
throw new IllegalArgumentException("Stream interval can't be negative");
}
internal.setFlushInterval(interval);
}
/**
* Returns how often events are made available for streaming purposes.
*
* @return the flush interval, or {@code null} if no interval has been set
*
* @since 14
*/
public Duration getFlushInterval() {
return internal.getFlushInterval();
}
/** /**
* Determines how far back data is kept in the disk repository. * Determines how far back data is kept in the disk repository.
* <p> * <p>
......
...@@ -601,6 +601,36 @@ ...@@ -601,6 +601,36 @@
<setting name="enabled">true</setting> <setting name="enabled">true</setting>
</event> </event>
<event name="jdk.Flush">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushStorage">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushStacktrace">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushStringPool">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushMetadata">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushTypeSet">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.DataLoss"> <event name="jdk.DataLoss">
<setting name="enabled">true</setting> <setting name="enabled">true</setting>
</event> </event>
......
...@@ -601,6 +601,36 @@ ...@@ -601,6 +601,36 @@
<setting name="enabled">true</setting> <setting name="enabled">true</setting>
</event> </event>
<event name="jdk.Flush">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushStorage">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushStacktrace">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushStringPool">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushMetadata">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.FlushTypeSet">
<setting name="enabled">true</setting>
<setting name="threshold">0 ns</setting>
</event>
<event name="jdk.DataLoss"> <event name="jdk.DataLoss">
<setting name="enabled">true</setting> <setting name="enabled">true</setting>
</event> </event>
......
/*
* Copyright (c) 2016, 2018, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.consumer;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import jdk.jfr.EventType;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.RecordingInput;
/**
* Parses a chunk.
*
*/
final class ChunkParser {
private static final long CONSTANT_POOL_TYPE_ID = 1;
private final RecordingInput input;
private final LongMap<Parser> parsers;
private final ChunkHeader chunkHeader;
private final long absoluteChunkEnd;
private final MetadataDescriptor metadata;
private final LongMap<Type> typeMap;
private final TimeConverter timeConverter;
public ChunkParser(RecordingInput input) throws IOException {
this(new ChunkHeader(input));
}
private ChunkParser(ChunkHeader header) throws IOException {
this.input = header.getInput();
this.chunkHeader = header;
this.metadata = header.readMetadata();
this.absoluteChunkEnd = header.getEnd();
this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
ParserFactory factory = new ParserFactory(metadata, timeConverter);
LongMap<ConstantMap> constantPools = factory.getConstantPools();
parsers = factory.getParsers();
typeMap = factory.getTypeMap();
fillConstantPools(parsers, constantPools);
constantPools.forEach(ConstantMap::setIsResolving);
constantPools.forEach(ConstantMap::resolve);
constantPools.forEach(ConstantMap::setResolved);
input.position(chunkHeader.getEventStart());
}
public RecordedEvent readEvent() throws IOException {
while (input.position() < absoluteChunkEnd) {
long pos = input.position();
int size = input.readInt();
if (size == 0) {
throw new IOException("Event can't have zero size");
}
long typeId = input.readLong();
if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0)
Parser ep = parsers.get(typeId);
if (ep instanceof EventParser) {
return (RecordedEvent) ep.parse(input);
}
}
input.position(pos + size);
}
return null;
}
private void fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools) throws IOException {
long nextCP = chunkHeader.getAbsoluteChunkStart();
long deltaToNext = chunkHeader.getConstantPoolPosition();
while (deltaToNext != 0) {
nextCP += deltaToNext;
input.position(nextCP);
final long position = nextCP;
int size = input.readInt(); // size
long typeId = input.readLong();
if (typeId != CONSTANT_POOL_TYPE_ID) {
throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId);
}
input.readLong(); // timestamp
input.readLong(); // duration
deltaToNext = input.readLong();
final long delta = deltaToNext;
boolean flush = input.readBoolean();
int poolCount = input.readInt();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
return "New constant pool: startPosition=" + position + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount;
});
for (int i = 0; i < poolCount; i++) {
long id = input.readLong(); // type id
ConstantMap pool = constantPools.get(id);
Type type = typeMap.get(id);
if (pool == null) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
if (type == null) {
throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]");
}
pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
constantPools.put(type.getId(), pool);
}
Parser parser = typeParser.get(id);
if (parser == null) {
throw new IOException("Could not find constant pool type with id = " + id);
}
try {
int count = input.readInt();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]");
for (int j = 0; j < count; j++) {
long key = input.readLong();
Object value = parser.parse(input);
pool.put(key, value);
}
} catch (Exception e) {
throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e);
}
}
if (input.position() != nextCP + size) {
throw new IOException("Size of check point event doesn't match content");
}
}
}
private String getName(long id) {
Type type = typeMap.get(id);
return type == null ? ("unknown(" + id + ")") : type.getName();
}
public Collection<Type> getTypes() {
return metadata.getTypes();
}
public List<EventType> getEventTypes() {
return metadata.getEventTypes();
}
public boolean isLastChunk() {
return chunkHeader.isLastChunk();
}
public ChunkParser nextChunkParser() throws IOException {
return new ChunkParser(chunkHeader.nextHeader());
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.consumer;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.function.Consumer;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Utils;
import jdk.jfr.internal.consumer.EventDirectoryStream;
import jdk.jfr.internal.consumer.EventFileStream;
import jdk.jfr.internal.consumer.FileAccess;
/**
* Represents a stream of events.
* <p>
* A stream is a sequence of events and the way to interact with a stream is to
* register actions. The {@code EventStream} interface is not to be implemented
* and future versions of the JDK may prevent this completely.
* <p>
* To receive a notification when an event arrives, register an action using the
* {@link #onEvent(Consumer)} method. To filter the stream for an event with a
* specific name, use {@link #onEvent(String, Consumer)} method.
* <p>
* By default, the same {@code RecordedEvent} object can be used to
* represent two or more distinct events. That object can be delivered
* multiple times to the same action as well as to other actions. To use an
* event object after the action is completed, the
* {@link #setReuse(boolean)} method should be set to {@code false} so a
* new object is allocated for each event.
* <p>
* Events are delivered in batches. To receive a notification when a batch is
* complete, register an action using the {@link #onFlush(Runnable)} method.
* This is an opportunity to aggregate or push data to external systems while
* the Java Virtual Machine (JVM) is preparing the next batch.
* <p>
* Events within a batch are sorted chronologically by their end time.
* Well-ordering of events is only maintained for events available to the JVM at
* the point of flush, i.e. for the set of events delivered as a unit in a
* single batch. Events delivered in a batch could therefore be out-of-order
* compared to events delivered in a previous batch, but never out-of-order with
* events within the same batch. If ordering is not a concern, sorting can be
* disabled using the {@link #setOrdered(boolean)} method.
* <p>
* To dispatch events to registered actions, the stream must be started. To
* start processing in the current thread, invoke the {@link #start()} method.
* To process actions asynchronously in a separate thread, invoke the
* {@link #startAsync()} method. To await completion of the stream, use the
* awaitTermination {@link #awaitTermination()} or the
* {@link #awaitTermination(Duration)} method.
* <p>
* When a stream ends it is automatically closed. To manually stop processing of
* events, close the stream by invoking the {@link #close()} method. A stream
* can also be automatically closed in exceptional circumstances, for example if
* the JVM that is being monitored exits. To receive a notification in any of
* these occasions, use the {@link #onClose(Runnable)} method to register an
* action.
* <p>
* If an unexpected exception occurs in an action, it is possible to catch the
* exception in an error handler. An error handler can be registered using the
* {@link #onError(Runnable)} method. If no error handler is registered, the
* default behavior is to print the exception and its backtrace to the standard
* error stream.
* <p>
* The following example shows how an {@code EventStream} can be used to listen
* to events on a JVM running Flight Recorder
*
* <pre>
* <code>
* try (var es = EventStream.openRepository()) {
* es.onEvent("jdk.CPULoad", event -> {
* System.out.println("CPU Load " + event.getEndTime());
* System.out.println(" Machine total: " + 100 * event.getFloat("machineTotal") + "%");
* System.out.println(" JVM User: " + 100 * event.getFloat("jvmUser") + "%");
* System.out.println(" JVM System: " + 100 * event.getFloat("jvmSystem") + "%");
* System.out.println();
* });
* es.onEvent("jdk.GarbageCollection", event -> {
* System.out.println("Garbage collection: " + event.getLong("gcId"));
* System.out.println(" Cause: " + event.getString("cause"));
* System.out.println(" Total pause: " + event.getDuration("sumOfPauses"));
* System.out.println(" Longest pause: " + event.getDuration("longestPause"));
* System.out.println();
* });
* es.start();
* }
* </code>
* </pre>
* <p>
* To start recording together with the stream, see {@link RecordingStream}.
*
* @since 14
*/
public interface EventStream extends AutoCloseable {
/**
* Creates a stream from the repository of the current Java Virtual Machine
* (JVM).
* <p>
* By default, the stream starts with the next event flushed by Flight
* Recorder.
*
* @return an event stream, not {@code null}
*
* @throws IOException if a stream can't be opened, or an I/O error occurs
* when trying to access the repository
*
* @throws SecurityException if a security manager exists and the caller
* does not have
* {@code FlightRecorderPermission("accessFlightRecorder")}
*/
public static EventStream openRepository() throws IOException {
Utils.checkAccessFlightRecorder();
return new EventDirectoryStream(AccessController.getContext(), null, SecuritySupport.PRIVILIGED, false);
}
/**
* Creates an event stream from a disk repository.
* <p>
* By default, the stream starts with the next event flushed by Flight
* Recorder.
*
* @param directory location of the disk repository, not {@code null}
*
* @return an event stream, not {@code null}
*
* @throws IOException if a stream can't be opened, or an I/O error occurs
* when trying to access the repository
*
* @throws SecurityException if a security manager exists and its
* {@code checkRead} method denies read access to the directory, or
* files in the directory.
*/
public static EventStream openRepository(Path directory) throws IOException {
Objects.nonNull(directory);
AccessControlContext acc = AccessController.getContext();
return new EventDirectoryStream(acc, directory, FileAccess.UNPRIVILIGED, false);
}
/**
* Creates an event stream from a file.
* <p>
* By default, the stream starts with the first event in the file.
*
* @param file location of the file, not {@code null}
*
* @return an event stream, not {@code null}
*
* @throws IOException if the file can't be opened, or an I/O error occurs
* during reading
*
* @throws SecurityException if a security manager exists and its
* {@code checkRead} method denies read access to the file
*/
static EventStream openFile(Path file) throws IOException {
return new EventFileStream(AccessController.getContext(), file);
}
/**
* Registers an action to perform on all events in the stream.
*
* @param action an action to perform on each {@code RecordedEvent}, not
* {@code null}
*/
void onEvent(Consumer<RecordedEvent> action);
/**
* Registers an action to perform on all events matching a name.
*
* @param eventName the name of the event, not {@code null}
*
* @param action an action to perform on each {@code RecordedEvent} matching
* the event name, not {@code null}
*/
void onEvent(String eventName, Consumer<RecordedEvent> action);
/**
* Registers an action to perform after the stream has been flushed.
*
* @param action an action to perform after the stream has been
* flushed, not {@code null}
*/
void onFlush(Runnable action);
/**
* Registers an action to perform if an exception occurs.
* <p>
* if an action is not registered, an exception stack trace is printed to
* standard error.
* <p>
* Registering an action overrides the default behavior. If multiple actions
* have been registered, they are performed in the order of registration.
* <p>
* If this method itself throws an exception, resulting behavior is
* undefined.
*
* @param action an action to perform if an exception occurs, not
* {@code null}
*/
void onError(Consumer<Throwable> action);
/**
* Registers an action to perform when the stream is closed.
* <p>
* If the stream is already closed, the action will be performed immediately
* in the current thread.
*
* @param action an action to perform after the stream is closed, not
* {@code null}
* @see #close()
*/
void onClose(Runnable action);
/**
* Releases all resources associated with this stream.
* <p>
* Closing a previously closed stream has no effect.
*/
void close();
/**
* Unregisters an action.
* <p>
* If the action has been registered multiple times, all instances are
* unregistered.
*
* @param action the action to unregister, not {@code null}
*
* @return {@code true} if the action was unregistered, {@code false}
* otherwise
*
* @see #onEvent(Consumer)
* @see #onEvent(String, Consumer)
* @see #onFlush(Runnable)
* @see #onClose(Runnable)
* @see #onError(Consumer)
*/
boolean remove(Object action);
/**
* Specifies that the event object in an {@link #onEvent(Consumer)} action
* can be reused.
* <p>
* If reuse is set to {@code true), an action should not keep a reference
* to the event object after the action has completed.
*
* @param reuse {@code true} if an event object can be reused, {@code false}
* otherwise
*/
void setReuse(boolean reuse);
/**
* Specifies that events arrives in chronological order, sorted by the time
* they were committed to the stream.
*
* @param ordered if event objects arrive in chronological order to
* {@code #onEvent(Consumer)}
*/
void setOrdered(boolean ordered);
/**
* Specifies the start time of the stream.
* <p>
* The start time must be set before starting the stream
*
* @param startTime the start time, not {@code null}
*
* @throws IllegalStateException if the stream is already started
*
* @see #start()
* @see #startAsync()
*/
void setStartTime(Instant startTime);
/**
* Specifies the end time of the stream.
* <p>
* The end time must be set before starting the stream.
* <p>
* At end time, the stream is closed.
*
* @param endTime the end time, not {@code null}
*
* @throws IllegalStateException if the stream is already started
*
* @see #start()
* @see #startAsync()
*/
void setEndTime(Instant endTime);
/**
* Start processing of actions.
* <p>
* Actions are performed in the current thread.
*
* @throws IllegalStateException if the stream is already started or closed
*/
void start();
/**
* Start asynchronous processing of actions.
* <p>
* Actions are performed in a single separate thread.
*
* @throws IllegalStateException if the stream is already started or closed
*/
void startAsync();
/**
* Blocks until all actions are completed, or the stream is closed, or the
* timeout occurs, or the current thread is interrupted, whichever happens
* first.
*
* @param timeout the maximum time to wait, not {@code null}
*
* @throws IllegalArgumentException if timeout is negative
* @throws InterruptedException if interrupted while waiting
*
* @see #start()
* @see #startAsync()
* @see Thread#interrupt()
*/
void awaitTermination(Duration timeout) throws InterruptedException;
/**
* Blocks until all actions are completed, or the stream is closed, or the
* current thread is interrupted, whichever happens first.
*
* @throws InterruptedException if interrupted while waiting
*
* @see #start()
* @see #startAsync()
* @see Thread#interrupt()
*/
void awaitTermination() throws InterruptedException;
}
...@@ -26,10 +26,8 @@ ...@@ -26,10 +26,8 @@
package jdk.jfr.consumer; package jdk.jfr.consumer;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List;
import jdk.jfr.ValueDescriptor; import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.internal.Type;
/** /**
* A recorded Java type, such as a class or an interface. * A recorded Java type, such as a class or an interface.
...@@ -37,21 +35,11 @@ import jdk.jfr.internal.Type; ...@@ -37,21 +35,11 @@ import jdk.jfr.internal.Type;
* @since 8 * @since 8
*/ */
public final class RecordedClass extends RecordedObject { public final class RecordedClass extends RecordedObject {
static ObjectFactory<RecordedClass> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedClass>(type) {
@Override
RecordedClass createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedClass(desc, id, object, timeConverter);
}
};
}
private final long uniqueId; private final long uniqueId;
// package private // package private
private RecordedClass(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) { RecordedClass(ObjectContext objectContext, long id, Object[] values) {
super(descriptors, values, timeConverter); super(objectContext, values);
this.uniqueId = id; this.uniqueId = id;
} }
......
...@@ -25,10 +25,7 @@ ...@@ -25,10 +25,7 @@
package jdk.jfr.consumer; package jdk.jfr.consumer;
import java.util.List; import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/** /**
* A recorded Java class loader. * A recorded Java class loader.
...@@ -36,21 +33,11 @@ import jdk.jfr.internal.Type; ...@@ -36,21 +33,11 @@ import jdk.jfr.internal.Type;
* @since 8 * @since 8
*/ */
public final class RecordedClassLoader extends RecordedObject { public final class RecordedClassLoader extends RecordedObject {
static ObjectFactory<RecordedClassLoader> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedClassLoader>(type) {
@Override
RecordedClassLoader createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedClassLoader(desc, id, object, timeConverter);
}
};
}
private final long uniqueId; private final long uniqueId;
// package private // package private
private RecordedClassLoader(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) { RecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
super(descriptors, values, timeConverter); super(objectContext, values);
this.uniqueId = id; this.uniqueId = id;
} }
......
...@@ -32,6 +32,7 @@ import java.util.List; ...@@ -32,6 +32,7 @@ import java.util.List;
import jdk.jfr.EventType; import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.EventInstrumentation; import jdk.jfr.internal.EventInstrumentation;
import jdk.jfr.internal.consumer.ObjectContext;
/** /**
* A recorded event. * A recorded event.
...@@ -39,17 +40,14 @@ import jdk.jfr.internal.EventInstrumentation; ...@@ -39,17 +40,14 @@ import jdk.jfr.internal.EventInstrumentation;
* @since 8 * @since 8
*/ */
public final class RecordedEvent extends RecordedObject { public final class RecordedEvent extends RecordedObject {
private final EventType eventType; long startTimeTicks;
private final long startTime; long endTimeTicks;
// package private needed for efficient sorting
final long endTime;
// package private // package private
RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) { RecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
super(vds, values, timeConverter); super(objectContext, values);
this.eventType = type; this.startTimeTicks = startTimeTicks;
this.startTime = startTime; this.endTimeTicks = endTimeTicks;
this.endTime = endTime;
} }
/** /**
...@@ -78,7 +76,7 @@ public final class RecordedEvent extends RecordedObject { ...@@ -78,7 +76,7 @@ public final class RecordedEvent extends RecordedObject {
* @return the event type, not {@code null} * @return the event type, not {@code null}
*/ */
public EventType getEventType() { public EventType getEventType() {
return eventType; return objectContext.eventType;
} }
/** /**
...@@ -89,7 +87,7 @@ public final class RecordedEvent extends RecordedObject { ...@@ -89,7 +87,7 @@ public final class RecordedEvent extends RecordedObject {
* @return the start time, not {@code null} * @return the start time, not {@code null}
*/ */
public Instant getStartTime() { public Instant getStartTime() {
return Instant.ofEpochSecond(0, startTime); return Instant.ofEpochSecond(0, getStartTimeNanos());
} }
/** /**
...@@ -100,7 +98,7 @@ public final class RecordedEvent extends RecordedObject { ...@@ -100,7 +98,7 @@ public final class RecordedEvent extends RecordedObject {
* @return the end time, not {@code null} * @return the end time, not {@code null}
*/ */
public Instant getEndTime() { public Instant getEndTime() {
return Instant.ofEpochSecond(0, endTime); return Instant.ofEpochSecond(0, getEndTimeNanos());
} }
/** /**
...@@ -109,7 +107,7 @@ public final class RecordedEvent extends RecordedObject { ...@@ -109,7 +107,7 @@ public final class RecordedEvent extends RecordedObject {
* @return the duration in nanoseconds, not {@code null} * @return the duration in nanoseconds, not {@code null}
*/ */
public Duration getDuration() { public Duration getDuration() {
return Duration.ofNanos(endTime - startTime); return Duration.ofNanos(getEndTimeNanos() - getStartTimeNanos());
} }
/** /**
...@@ -119,6 +117,31 @@ public final class RecordedEvent extends RecordedObject { ...@@ -119,6 +117,31 @@ public final class RecordedEvent extends RecordedObject {
*/ */
@Override @Override
public List<ValueDescriptor> getFields() { public List<ValueDescriptor> getFields() {
return getEventType().getFields(); return objectContext.fields;
}
protected final Object objectAt(int index) {
if (index == 0) {
return startTimeTicks;
}
if (hasDuration()) {
if (index == 1) {
return endTimeTicks - startTimeTicks;
}
return objects[index - 2];
}
return objects[index - 1];
}
private boolean hasDuration() {
return objects.length + 2 == objectContext.fields.size();
}
private long getStartTimeNanos() {
return objectContext.convertTimestamp(startTimeTicks);
}
private long getEndTimeNanos() {
return objectContext.convertTimestamp(endTimeTicks);
} }
} }
...@@ -26,10 +26,8 @@ ...@@ -26,10 +26,8 @@
package jdk.jfr.consumer; package jdk.jfr.consumer;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List;
import jdk.jfr.ValueDescriptor; import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.internal.Type;
/** /**
* A recorded frame in a stack trace. * A recorded frame in a stack trace.
...@@ -37,19 +35,9 @@ import jdk.jfr.internal.Type; ...@@ -37,19 +35,9 @@ import jdk.jfr.internal.Type;
* @since 8 * @since 8
*/ */
public final class RecordedFrame extends RecordedObject { public final class RecordedFrame extends RecordedObject {
static ObjectFactory<RecordedFrame> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedFrame>(type) {
@Override
RecordedFrame createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedFrame(desc, object, timeConverter);
}
};
}
// package private // package private
RecordedFrame(List<ValueDescriptor> desc, Object[] objects, TimeConverter timeConverter) { RecordedFrame(ObjectContext objectContext, Object[] values) {
super(desc, objects, timeConverter); super(objectContext, values);
} }
/** /**
......
...@@ -26,10 +26,8 @@ ...@@ -26,10 +26,8 @@
package jdk.jfr.consumer; package jdk.jfr.consumer;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List;
import jdk.jfr.ValueDescriptor; import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.internal.Type;
/** /**
* A recorded method. * A recorded method.
...@@ -38,17 +36,9 @@ import jdk.jfr.internal.Type; ...@@ -38,17 +36,9 @@ import jdk.jfr.internal.Type;
*/ */
public final class RecordedMethod extends RecordedObject { public final class RecordedMethod extends RecordedObject {
static ObjectFactory<RecordedMethod> createFactory(Type type, TimeConverter timeConverter) { // package private
return new ObjectFactory<RecordedMethod>(type) { RecordedMethod(ObjectContext objectContext, Object[] values) {
@Override super(objectContext, values);
RecordedMethod createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedMethod(desc, object, timeConverter);
}
};
}
private RecordedMethod(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
super(descriptors, objects, timeConverter);
} }
/** /**
......
...@@ -25,18 +25,24 @@ ...@@ -25,18 +25,24 @@
package jdk.jfr.consumer; package jdk.jfr.consumer;
import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import jdk.jfr.Timespan; import jdk.jfr.Timespan;
import jdk.jfr.Timestamp; import jdk.jfr.Timestamp;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.consumer.JdkJfrConsumer;
import jdk.jfr.internal.consumer.ObjectFactory;
import jdk.jfr.internal.PrivateAccess; import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.internal.tool.PrettyWriter; import jdk.jfr.internal.tool.PrettyWriter;
/** /**
...@@ -51,6 +57,89 @@ import jdk.jfr.internal.tool.PrettyWriter; ...@@ -51,6 +57,89 @@ import jdk.jfr.internal.tool.PrettyWriter;
*/ */
public class RecordedObject { public class RecordedObject {
static{
JdkJfrConsumer access = new JdkJfrConsumer() {
public List<Type> readTypes(RecordingFile file) throws IOException {
return file.readTypes();
}
public boolean isLastEventInChunk(RecordingFile file) {
return file.isLastEventInChunk();
}
@Override
public Object getOffsetDataTime(RecordedObject event, String name) {
return event.getOffsetDateTime(name);
}
@Override
public RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values) {
return new RecordedClass(objectContext, id, values);
}
@Override
public RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
return new RecordedClassLoader(objectContext, id, values);
}
@Override
public Comparator<? super RecordedEvent> eventComparator() {
return new Comparator<RecordedEvent>() {
@Override
public int compare(RecordedEvent e1, RecordedEvent e2) {
return Long.compare(e1.endTimeTicks, e2.endTimeTicks);
}
};
}
@Override
public RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values) {
return new RecordedStackTrace(objectContext, values);
}
@Override
public RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values) {
return new RecordedThreadGroup(objectContext, values);
}
@Override
public RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values) {
return new RecordedFrame(objectContext, values);
}
@Override
public RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values) {
return new RecordedThread(objectContext, id, values);
}
@Override
public RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values) {
return new RecordedMethod(objectContext, values);
}
@Override
public RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
return new RecordedEvent(objectContext, values, startTimeTicks, endTimeTicks);
}
@Override
public void setStartTicks(RecordedEvent event, long startTicks) {
event.startTimeTicks = startTicks;
}
@Override
public void setEndTicks(RecordedEvent event, long endTicks) {
event.endTimeTicks = endTicks;
}
@Override
public Object[] eventValues(RecordedEvent event) {
return event.objects;
}
};
JdkJfrConsumer.setAccess(access);
}
private final static class UnsignedValue { private final static class UnsignedValue {
private final Object o; private final Object o;
...@@ -63,15 +152,13 @@ public class RecordedObject { ...@@ -63,15 +152,13 @@ public class RecordedObject {
} }
} }
private final Object[] objects; final Object[] objects;
private final List<ValueDescriptor> descriptors; final ObjectContext objectContext;
private final TimeConverter timeConverter;
// package private, not to be subclassed outside this package // package private, not to be subclassed outside this package
RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) { RecordedObject(ObjectContext objectContext, Object[] objects) {
this.descriptors = descriptors; this.objectContext = objectContext;
this.objects = objects; this.objects = objects;
this.timeConverter = timeConverter;
} }
// package private // package private
...@@ -101,7 +188,7 @@ public class RecordedObject { ...@@ -101,7 +188,7 @@ public class RecordedObject {
*/ */
public boolean hasField(String name) { public boolean hasField(String name) {
Objects.requireNonNull(name); Objects.requireNonNull(name);
for (ValueDescriptor v : descriptors) { for (ValueDescriptor v : objectContext.fields) {
if (v.getName().equals(name)) { if (v.getName().equals(name)) {
return true; return true;
} }
...@@ -109,7 +196,7 @@ public class RecordedObject { ...@@ -109,7 +196,7 @@ public class RecordedObject {
int dotIndex = name.indexOf("."); int dotIndex = name.indexOf(".");
if (dotIndex > 0) { if (dotIndex > 0) {
String structName = name.substring(0, dotIndex); String structName = name.substring(0, dotIndex);
for (ValueDescriptor v : descriptors) { for (ValueDescriptor v : objectContext.fields) {
if (!v.getFields().isEmpty() && v.getName().equals(structName)) { if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
RecordedObject child = getValue(structName); RecordedObject child = getValue(structName);
if (child != null) { if (child != null) {
...@@ -169,12 +256,16 @@ public class RecordedObject { ...@@ -169,12 +256,16 @@ public class RecordedObject {
return t; return t;
} }
protected Object objectAt(int index) {
return objects[index];
}
private Object getValue(String name, boolean allowUnsigned) { private Object getValue(String name, boolean allowUnsigned) {
Objects.requireNonNull(name); Objects.requireNonNull(name);
int index = 0; int index = 0;
for (ValueDescriptor v : descriptors) { for (ValueDescriptor v : objectContext.fields) {
if (name.equals(v.getName())) { if (name.equals(v.getName())) {
Object object = objects[index]; Object object = objectAt(index);
if (object == null) { if (object == null) {
// error or missing // error or missing
return null; return null;
...@@ -200,7 +291,7 @@ public class RecordedObject { ...@@ -200,7 +291,7 @@ public class RecordedObject {
return structifyArray(v, array, 0); return structifyArray(v, array, 0);
} }
// struct // struct
return new RecordedObject(v.getFields(), (Object[]) object, timeConverter); return new RecordedObject(objectContext.getInstance(v), (Object[]) object);
} }
} }
index++; index++;
...@@ -209,7 +300,7 @@ public class RecordedObject { ...@@ -209,7 +300,7 @@ public class RecordedObject {
int dotIndex = name.indexOf("."); int dotIndex = name.indexOf(".");
if (dotIndex > 0) { if (dotIndex > 0) {
String structName = name.substring(0, dotIndex); String structName = name.substring(0, dotIndex);
for (ValueDescriptor v : descriptors) { for (ValueDescriptor v : objectContext.fields) {
if (!v.getFields().isEmpty() && v.getName().equals(structName)) { if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
RecordedObject child = getValue(structName); RecordedObject child = getValue(structName);
String subName = name.substring(dotIndex + 1); String subName = name.substring(dotIndex + 1);
...@@ -261,7 +352,7 @@ public class RecordedObject { ...@@ -261,7 +352,7 @@ public class RecordedObject {
private <T> T getTypedValue(String name, String typeName) { private <T> T getTypedValue(String name, String typeName) {
Objects.requireNonNull(name); Objects.requireNonNull(name);
// Validate name and type first // Validate name and type first
getValueDescriptor(descriptors, name, typeName); getValueDescriptor(objectContext.fields, name, typeName);
return getValue(name); return getValue(name);
} }
...@@ -270,15 +361,16 @@ public class RecordedObject { ...@@ -270,15 +361,16 @@ public class RecordedObject {
return null; return null;
} }
Object[] structArray = new Object[array.length]; Object[] structArray = new Object[array.length];
ObjectContext objContext = objectContext.getInstance(v);
for (int i = 0; i < structArray.length; i++) { for (int i = 0; i < structArray.length; i++) {
Object arrayElement = array[i]; Object arrayElement = array[i];
if (dimension == 0) { if (dimension == 0) {
// No general way to handle structarrays // No general way to handle structarrays
// without invoking ObjectFactory for every instance (which may require id) // without invoking ObjectFactory for every instance (which may require id)
if (isStackFrameType(v.getTypeName())) { if (isStackFrameType(v.getTypeName())) {
structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter); structArray[i] = new RecordedFrame(objContext, (Object[]) arrayElement);
} else { } else {
structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter); structArray[i] = new RecordedObject(objContext, (Object[]) arrayElement);
} }
} else { } else {
structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1); structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
...@@ -303,7 +395,7 @@ public class RecordedObject { ...@@ -303,7 +395,7 @@ public class RecordedObject {
* @return the fields, not {@code null} * @return the fields, not {@code null}
*/ */
public List<ValueDescriptor> getFields() { public List<ValueDescriptor> getFields() {
return descriptors; return objectContext.fields;
} }
/** /**
...@@ -725,7 +817,7 @@ public class RecordedObject { ...@@ -725,7 +817,7 @@ public class RecordedObject {
} }
private Duration getDuration(long timespan, String name) throws InternalError { private Duration getDuration(long timespan, String name) throws InternalError {
ValueDescriptor v = getValueDescriptor(descriptors, name, null); ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
if (timespan == Long.MIN_VALUE) { if (timespan == Long.MIN_VALUE) {
return Duration.ofSeconds(Long.MIN_VALUE, 0); return Duration.ofSeconds(Long.MIN_VALUE, 0);
} }
...@@ -741,7 +833,7 @@ public class RecordedObject { ...@@ -741,7 +833,7 @@ public class RecordedObject {
case Timespan.NANOSECONDS: case Timespan.NANOSECONDS:
return Duration.ofNanos(timespan); return Duration.ofNanos(timespan);
case Timespan.TICKS: case Timespan.TICKS:
return Duration.ofNanos(timeConverter.convertTimespan(timespan)); return Duration.ofNanos(objectContext.convertTimespan(timespan));
} }
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value()); throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
} }
...@@ -804,7 +896,7 @@ public class RecordedObject { ...@@ -804,7 +896,7 @@ public class RecordedObject {
} }
private Instant getInstant(long timestamp, String name) { private Instant getInstant(long timestamp, String name) {
ValueDescriptor v = getValueDescriptor(descriptors, name, null); ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
Timestamp ts = v.getAnnotation(Timestamp.class); Timestamp ts = v.getAnnotation(Timestamp.class);
if (ts != null) { if (ts != null) {
if (timestamp == Long.MIN_VALUE) { if (timestamp == Long.MIN_VALUE) {
...@@ -814,7 +906,7 @@ public class RecordedObject { ...@@ -814,7 +906,7 @@ public class RecordedObject {
case Timestamp.MILLISECONDS_SINCE_EPOCH: case Timestamp.MILLISECONDS_SINCE_EPOCH:
return Instant.ofEpochMilli(timestamp); return Instant.ofEpochMilli(timestamp);
case Timestamp.TICKS: case Timestamp.TICKS:
return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp)); return Instant.ofEpochSecond(0, objectContext.convertTimestamp(timestamp));
} }
throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value()); throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
} }
...@@ -889,12 +981,12 @@ public class RecordedObject { ...@@ -889,12 +981,12 @@ public class RecordedObject {
} }
// package private for now. Used by EventWriter // package private for now. Used by EventWriter
OffsetDateTime getOffsetDateTime(String name) { private OffsetDateTime getOffsetDateTime(String name) {
Instant instant = getInstant(name); Instant instant = getInstant(name);
if (instant.equals(Instant.MIN)) { if (instant.equals(Instant.MIN)) {
return OffsetDateTime.MIN; return OffsetDateTime.MIN;
} }
return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset()); return OffsetDateTime.ofInstant(getInstant(name), objectContext.getZoneOffset());
} }
private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) { private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
......
...@@ -29,8 +29,7 @@ import java.util.Arrays; ...@@ -29,8 +29,7 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import jdk.jfr.ValueDescriptor; import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.internal.Type;
/** /**
* A recorded stack trace. * A recorded stack trace.
...@@ -38,18 +37,9 @@ import jdk.jfr.internal.Type; ...@@ -38,18 +37,9 @@ import jdk.jfr.internal.Type;
* @since 8 * @since 8
*/ */
public final class RecordedStackTrace extends RecordedObject { public final class RecordedStackTrace extends RecordedObject {
// package private
static ObjectFactory<RecordedStackTrace> createFactory(Type type, TimeConverter timeConverter) { RecordedStackTrace(ObjectContext objectContext, Object[] values) {
return new ObjectFactory<RecordedStackTrace>(type) { super(objectContext, values);
@Override
RecordedStackTrace createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedStackTrace(desc, object, timeConverter);
}
};
}
private RecordedStackTrace(List<ValueDescriptor> desc, Object[] values, TimeConverter timeConverter) {
super(desc, values, timeConverter);
} }
/** /**
......
...@@ -25,10 +25,7 @@ ...@@ -25,10 +25,7 @@
package jdk.jfr.consumer; package jdk.jfr.consumer;
import java.util.List; import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/** /**
* A recorded thread. * A recorded thread.
...@@ -36,20 +33,11 @@ import jdk.jfr.internal.Type; ...@@ -36,20 +33,11 @@ import jdk.jfr.internal.Type;
* @since 8 * @since 8
*/ */
public final class RecordedThread extends RecordedObject { public final class RecordedThread extends RecordedObject {
static ObjectFactory<RecordedThread> createFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedThread>(type) {
@Override
RecordedThread createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedThread(desc, id, object, timeConverter);
}
};
}
private final long uniqueId; private final long uniqueId;
private RecordedThread(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) { // package private
super(descriptors, values, timeConverter); RecordedThread(ObjectContext objectContext, long id, Object[] values) {
super(objectContext, values);
this.uniqueId = id; this.uniqueId = id;
} }
......
...@@ -25,10 +25,7 @@ ...@@ -25,10 +25,7 @@
package jdk.jfr.consumer; package jdk.jfr.consumer;
import java.util.List; import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.Type;
/** /**
* A recorded Java thread group. * A recorded Java thread group.
...@@ -36,18 +33,9 @@ import jdk.jfr.internal.Type; ...@@ -36,18 +33,9 @@ import jdk.jfr.internal.Type;
* @since 8 * @since 8
*/ */
public final class RecordedThreadGroup extends RecordedObject { public final class RecordedThreadGroup extends RecordedObject {
// package private
static ObjectFactory<RecordedThreadGroup> createFactory(Type type, TimeConverter timeConverter) { RecordedThreadGroup(ObjectContext objectContext, Object[] values) {
return new ObjectFactory<RecordedThreadGroup>(type) { super(objectContext, values);
@Override
RecordedThreadGroup createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
return new RecordedThreadGroup(desc, object, timeConverter);
}
};
}
private RecordedThreadGroup(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
super(descriptors, objects, timeConverter);
} }
/** /**
......
...@@ -32,7 +32,6 @@ import java.io.IOException; ...@@ -32,7 +32,6 @@ import java.io.IOException;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
...@@ -40,8 +39,9 @@ import jdk.jfr.EventType; ...@@ -40,8 +39,9 @@ import jdk.jfr.EventType;
import jdk.jfr.internal.MetadataDescriptor; import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type; import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader; import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.ChunkParser;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput; import jdk.jfr.internal.consumer.RecordingInput;
import jdk.jfr.internal.consumer.RecordingInternals;
/** /**
* A recording file. * A recording file.
...@@ -62,27 +62,6 @@ import jdk.jfr.internal.consumer.RecordingInternals; ...@@ -62,27 +62,6 @@ import jdk.jfr.internal.consumer.RecordingInternals;
* @since 8 * @since 8
*/ */
public final class RecordingFile implements Closeable { public final class RecordingFile implements Closeable {
static{
RecordingInternals.INSTANCE = new RecordingInternals() {
public List<Type> readTypes(RecordingFile file) throws IOException {
return file.readTypes();
}
public boolean isLastEventInChunk(RecordingFile file) {
return file.isLastEventInChunk;
}
@Override
public Object getOffsetDataTime(RecordedObject event, String name) {
return event.getOffsetDateTime(name);
}
@Override
public void sort(List<RecordedEvent> events) {
Collections.sort(events, (e1, e2) -> Long.compare(e1.endTime, e2.endTime));
}
};
}
private boolean isLastEventInChunk; private boolean isLastEventInChunk;
private final File file; private final File file;
...@@ -104,7 +83,7 @@ public final class RecordingFile implements Closeable { ...@@ -104,7 +83,7 @@ public final class RecordingFile implements Closeable {
*/ */
public RecordingFile(Path file) throws IOException { public RecordingFile(Path file) throws IOException {
this.file = file.toFile(); this.file = file.toFile();
this.input = new RecordingInput(this.file); this.input = new RecordingInput(this.file, FileAccess.UNPRIVILIGED);
findNext(); findNext();
} }
...@@ -154,14 +133,15 @@ public final class RecordingFile implements Closeable { ...@@ -154,14 +133,15 @@ public final class RecordingFile implements Closeable {
*/ */
public List<EventType> readEventTypes() throws IOException { public List<EventType> readEventTypes() throws IOException {
ensureOpen(); ensureOpen();
MetadataDescriptor previous = null;
List<EventType> types = new ArrayList<>(); List<EventType> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>(); HashSet<Long> foundIds = new HashSet<>();
try (RecordingInput ri = new RecordingInput(file)) { try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILIGED)) {
ChunkHeader ch = new ChunkHeader(ri); ChunkHeader ch = new ChunkHeader(ri);
aggregateEventTypeForChunk(ch, types, foundIds); aggregateEventTypeForChunk(ch, null, types, foundIds);
while (!ch.isLastChunk()) { while (!ch.isLastChunk()) {
ch = ch.nextHeader(); ch = ch.nextHeader();
aggregateEventTypeForChunk(ch, types, foundIds); previous = aggregateEventTypeForChunk(ch, previous, types, foundIds);
} }
} }
return types; return types;
...@@ -169,37 +149,41 @@ public final class RecordingFile implements Closeable { ...@@ -169,37 +149,41 @@ public final class RecordingFile implements Closeable {
List<Type> readTypes() throws IOException { List<Type> readTypes() throws IOException {
ensureOpen(); ensureOpen();
MetadataDescriptor previous = null;
List<Type> types = new ArrayList<>(); List<Type> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>(); HashSet<Long> foundIds = new HashSet<>();
try (RecordingInput ri = new RecordingInput(file)) { try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILIGED)) {
ChunkHeader ch = new ChunkHeader(ri); ChunkHeader ch = new ChunkHeader(ri);
aggregateTypeForChunk(ch, types, foundIds); ch.awaitFinished();
aggregateTypeForChunk(ch, null, types, foundIds);
while (!ch.isLastChunk()) { while (!ch.isLastChunk()) {
ch = ch.nextHeader(); ch = ch.nextHeader();
aggregateTypeForChunk(ch, types, foundIds); previous = aggregateTypeForChunk(ch, previous, types, foundIds);
} }
} }
return types; return types;
} }
private void aggregateTypeForChunk(ChunkHeader ch, List<Type> types, HashSet<Long> foundIds) throws IOException { private MetadataDescriptor aggregateTypeForChunk(ChunkHeader ch, MetadataDescriptor previous, List<Type> types, HashSet<Long> foundIds) throws IOException {
MetadataDescriptor m = ch.readMetadata(); MetadataDescriptor m = ch.readMetadata(previous);
for (Type t : m.getTypes()) { for (Type t : m.getTypes()) {
if (!foundIds.contains(t.getId())) { if (!foundIds.contains(t.getId())) {
types.add(t); types.add(t);
foundIds.add(t.getId()); foundIds.add(t.getId());
} }
} }
return m;
} }
private static void aggregateEventTypeForChunk(ChunkHeader ch, List<EventType> types, HashSet<Long> foundIds) throws IOException { private static MetadataDescriptor aggregateEventTypeForChunk(ChunkHeader ch, MetadataDescriptor previous, List<EventType> types, HashSet<Long> foundIds) throws IOException {
MetadataDescriptor m = ch.readMetadata(); MetadataDescriptor m = ch.readMetadata(previous);
for (EventType t : m.getEventTypes()) { for (EventType t : m.getEventTypes()) {
if (!foundIds.contains(t.getId())) { if (!foundIds.contains(t.getId())) {
types.add(t); types.add(t);
foundIds.add(t.getId()); foundIds.add(t.getId());
} }
} }
return m;
} }
/** /**
...@@ -246,6 +230,17 @@ public final class RecordingFile implements Closeable { ...@@ -246,6 +230,17 @@ public final class RecordingFile implements Closeable {
} }
} }
// package protected
File getFile() {
return file;
}
// package protected
boolean isLastEventInChunk() {
return isLastEventInChunk;
}
// either sets next to an event or sets eof to true // either sets next to an event or sets eof to true
private void findNext() throws IOException { private void findNext() throws IOException {
while (nextEvent == null) { while (nextEvent == null) {
......
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.consumer;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.function.Consumer;
import jdk.jfr.Configuration;
import jdk.jfr.Event;
import jdk.jfr.EventSettings;
import jdk.jfr.EventType;
import jdk.jfr.Recording;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Utils;
import jdk.jfr.internal.consumer.EventDirectoryStream;
/**
* A recording stream produces events from the current JVM (Java Virtual
* Machine).
* <p>
* The following example shows how to record events using the default
* configuration and print the Garbage Collection, CPU Load and JVM Information
* event to standard out.
* <pre>
* <code>
* Configuration c = Configuration.getConfiguration("default");
* try (var rs = new RecordingStream(c)) {
* rs.onEvent("jdk.GarbageCollection", System.out::println);
* rs.onEvent("jdk.CPULoad", System.out::println);
* rs.onEvent("jdk.JVMInformation", System.out::println);
* rs.start();
* }
* }
* </code>
* </pre>
*
* @since 14
*/
public final class RecordingStream implements AutoCloseable, EventStream {
private final Recording recording;
private final EventDirectoryStream directoryStream;
/**
* Creates an event stream for the current JVM (Java Virtual Machine).
*
* @throws IllegalStateException if Flight Recorder can't be created (for
* example, if the Java Virtual Machine (JVM) lacks Flight Recorder
* support, or if the file repository can't be created or accessed)
*
* @throws SecurityException if a security manager exists and the caller
* does not have
* {@code FlightRecorderPermission("accessFlightRecorder")}
*/
public RecordingStream() {
Utils.checkAccessFlightRecorder();
AccessControlContext acc = AccessController.getContext();
this.recording = new Recording();
this.recording.setFlushInterval(Duration.ofMillis(1000));
try {
this.directoryStream = new EventDirectoryStream(acc, null, SecuritySupport.PRIVILIGED, true);
} catch (IOException ioe) {
this.recording.close();
throw new IllegalStateException(ioe.getMessage());
}
}
/**
* Creates a recording stream using settings from a configuration.
* <p>
* The following example shows how to create a recording stream that uses a
* predefined configuration.
*
* <pre>
* <code>
* var c = Configuration.getConfiguration("default");
* try (var rs = new RecordingStream(c)) {
* rs.onEvent(System.out::println);
* rs.start();
* }
* </code>
* </pre>
*
* @param configuration configuration that contains the settings to use,
* not {@code null}
*
* @throws IllegalStateException if Flight Recorder can't be created (for
* example, if the Java Virtual Machine (JVM) lacks Flight Recorder
* support, or if the file repository can't be created or accessed)
*
* @throws SecurityException if a security manager is used and
* FlightRecorderPermission "accessFlightRecorder" is not set.
*
* @see Configuration
*/
public RecordingStream(Configuration configuration) {
this();
recording.setSettings(configuration.getSettings());
}
/**
* Enables the event with the specified name.
* <p>
* If multiple events have the same name (for example, the same class is
* loaded in different class loaders), then all events that match the name
* are enabled. To enable a specific class, use the {@link #enable(Class)}
* method or a {@code String} representation of the event type ID.
*
* @param name the settings for the event, not {@code null}
*
* @return an event setting for further configuration, not {@code null}
*
* @see EventType
*/
public EventSettings enable(String name) {
return recording.enable(name);
}
/**
* Replaces all settings for this recording stream.
* <p>
* The following example records 20 seconds using the "default" configuration
* and then changes settings to the "profile" configuration.
*
* <pre>
* <code>
* Configuration defaultConfiguration = Configuration.getConfiguration("default");
* Configuration profileConfiguration = Configuration.getConfiguration("profile");
* try (var rs = new RecordingStream(defaultConfiguration) {
* rs.onEvent(System.out::println);
* rs.startAsync();
* Thread.sleep(20_000);
* rs.setSettings(profileConfiguration.getSettings());
* Thread.sleep(20_000);
* }
* </code>
* </pre>
*
* @param settings the settings to set, not {@code null}
*
* @see Recording#setSettings(Map)
*/
public void setSettings(Map<String, String> settings) {
recording.setSettings(settings);
};
/**
* Enables event.
*
* @param eventClass the event to enable, not {@code null}
*
* @throws IllegalArgumentException if {@code eventClass} is an abstract
* class or not a subclass of {@link Event}
*
* @return an event setting for further configuration, not {@code null}
*/
public EventSettings enable(Class<? extends Event> eventClass) {
return recording.enable(eventClass);
}
/**
* Disables event with the specified name.
* <p>
* If multiple events with same name (for example, the same class is loaded
* in different class loaders), then all events that match the name are
* disabled. To disable a specific class, use the {@link #disable(Class)}
* method or a {@code String} representation of the event type ID.
*
* @param name the settings for the event, not {@code null}
*
* @return an event setting for further configuration, not {@code null}
*
*/
public EventSettings disable(String name) {
return recording.disable(name);
}
/**
* Disables event.
*
* @param eventClass the event to enable, not {@code null}
*
* @throws IllegalArgumentException if {@code eventClass} is an abstract
* class or not a subclass of {@link Event}
*
* @return an event setting for further configuration, not {@code null}
*
*/
public EventSettings disable(Class<? extends Event> eventClass) {
return recording.disable(eventClass);
}
/**
* Determines how far back data is kept for the stream.
* <p>
* To control the amount of recording data stored on disk, the maximum
* length of time to retain the data can be specified. Data stored on disk
* that is older than the specified length of time is removed by the Java
* Virtual Machine (JVM).
* <p>
* If neither maximum limit or the maximum age is set, the size of the
* recording may grow indefinitely if events are on
*
* @param maxAge the length of time that data is kept, or {@code null} if
* infinite
*
* @throws IllegalArgumentException if {@code maxAge} is negative
*
* @throws IllegalStateException if the recording is in the {@code CLOSED}
* state
*/
public void setMaxAge(Duration maxAge) {
recording.setMaxAge(maxAge);
}
/**
* Determines how much data is kept for the stream.
* <p>
* To control the amount of recording data that is stored on disk, the
* maximum amount of data to retain can be specified. When the maximum limit
* is exceeded, the Java Virtual Machine (JVM) removes the oldest chunk to
* make room for a more recent chunk.
* <p>
* If neither maximum limit or the maximum age is set, the size of the
* recording may grow indefinitely.
* <p>
* The size is measured in bytes.
*
* @param maxSize the amount of data to retain, {@code 0} if infinite
*
* @throws IllegalArgumentException if {@code maxSize} is negative
*
* @throws IllegalStateException if the recording is in {@code CLOSED} state
*/
public void setMaxSize(long maxSize) {
recording.setMaxSize(maxSize);
}
/**
* Determines how often events are made available for streaming.
*
* @param interval the interval at which events are made available to the
* stream, no {@code null}
*
* @throws IllegalArgumentException if {@code interval} is negative
*
* @throws IllegalStateException if the stream is closed
*/
public void setFlushInterval(Duration interval) {
recording.setFlushInterval(interval);
}
@Override
public void setReuse(boolean reuse) {
directoryStream.setReuse(reuse);
}
@Override
public void setOrdered(boolean ordered) {
directoryStream.setOrdered(ordered);
}
@Override
public void setStartTime(Instant startTime) {
directoryStream.setStartTime(startTime);
}
@Override
public void setEndTime(Instant endTime) {
directoryStream.setEndTime(endTime);
}
@Override
public void onEvent(String eventName, Consumer<RecordedEvent> action) {
directoryStream.onEvent(eventName, action);
}
@Override
public void onEvent(Consumer<RecordedEvent> action) {
directoryStream.onEvent(action);
}
@Override
public void onFlush(Runnable action) {
directoryStream.onFlush(action);
}
@Override
public void onClose(Runnable action) {
directoryStream.onClose(action);
}
@Override
public void onError(Consumer<Throwable> action) {
directoryStream.onError(action);
}
@Override
public void close() {
recording.close();
directoryStream.close();
}
@Override
public boolean remove(Object action) {
return directoryStream.remove(action);
}
@Override
public void start() {
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
long startNanos = pr.start();
directoryStream.start(startNanos);
}
@Override
public void startAsync() {
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
long startNanos = pr.start();
directoryStream.startAsync(startNanos);
}
@Override
public void awaitTermination(Duration timeout) throws InterruptedException {
directoryStream.awaitTermination(timeout);
}
@Override
public void awaitTermination() throws InterruptedException {
directoryStream.awaitTermination();
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -40,6 +40,13 @@ import jdk.jfr.internal.Type; ...@@ -40,6 +40,13 @@ import jdk.jfr.internal.Type;
@StackTrace(false) @StackTrace(false)
public final class ActiveRecordingEvent extends AbstractJDKEvent { public final class ActiveRecordingEvent extends AbstractJDKEvent {
public static final ThreadLocal<ActiveRecordingEvent> EVENT = new ThreadLocal<ActiveRecordingEvent>() {
@Override
protected ActiveRecordingEvent initialValue() {
return new ActiveRecordingEvent();
}
};
@Label("Id") @Label("Id")
public long id; public long id;
...@@ -53,6 +60,10 @@ public final class ActiveRecordingEvent extends AbstractJDKEvent { ...@@ -53,6 +60,10 @@ public final class ActiveRecordingEvent extends AbstractJDKEvent {
@Timespan(Timespan.MILLISECONDS) @Timespan(Timespan.MILLISECONDS)
public long maxAge; public long maxAge;
@Label("Flush Interval")
@Timespan(Timespan.MILLISECONDS)
public long flushInterval;
@Label("Max Size") @Label("Max Size")
@DataAmount @DataAmount
public long maxSize; public long maxSize;
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -37,6 +37,13 @@ import jdk.jfr.internal.Type; ...@@ -37,6 +37,13 @@ import jdk.jfr.internal.Type;
@StackTrace(false) @StackTrace(false)
public final class ActiveSettingEvent extends AbstractJDKEvent { public final class ActiveSettingEvent extends AbstractJDKEvent {
public static final ThreadLocal<ActiveSettingEvent> EVENT = new ThreadLocal<ActiveSettingEvent>() {
@Override
protected ActiveSettingEvent initialValue() {
return new ActiveSettingEvent();
}
};
@Label("Event Id") @Label("Event Id")
public long id; public long id;
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -32,11 +32,7 @@ import java.lang.reflect.Method; ...@@ -32,11 +32,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import jdk.jfr.AnnotationElement; import jdk.jfr.AnnotationElement;
import jdk.jfr.Enabled; import jdk.jfr.Enabled;
...@@ -59,7 +55,14 @@ import jdk.jfr.internal.settings.ThresholdSetting; ...@@ -59,7 +55,14 @@ import jdk.jfr.internal.settings.ThresholdSetting;
// holds SettingControl instances that need to be released // holds SettingControl instances that need to be released
// when a class is unloaded (to avoid memory leaks). // when a class is unloaded (to avoid memory leaks).
public final class EventControl { public final class EventControl {
final static class NamedControl {
public final String name;
public final Control control;
NamedControl(String name, Control control) {
this.name = name;
this.control = control;
}
}
static final String FIELD_SETTING_PREFIX = "setting"; static final String FIELD_SETTING_PREFIX = "setting";
private static final Type TYPE_ENABLED = TypeLibrary.createType(EnabledSetting.class); private static final Type TYPE_ENABLED = TypeLibrary.createType(EnabledSetting.class);
private static final Type TYPE_THRESHOLD = TypeLibrary.createType(ThresholdSetting.class); private static final Type TYPE_THRESHOLD = TypeLibrary.createType(ThresholdSetting.class);
...@@ -67,24 +70,24 @@ public final class EventControl { ...@@ -67,24 +70,24 @@ public final class EventControl {
private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class); private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class);
private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class); private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class);
private final List<SettingInfo> settingInfos = new ArrayList<>(); private final ArrayList<SettingInfo> settingInfos = new ArrayList<>();
private final Map<String, Control> eventControls = new HashMap<>(5); private final ArrayList<NamedControl> namedControls = new ArrayList<>(5);
private final PlatformEventType type; private final PlatformEventType type;
private final String idName; private final String idName;
EventControl(PlatformEventType eventType) { EventControl(PlatformEventType eventType) {
eventControls.put(Enabled.NAME, defineEnabled(eventType)); addControl(Enabled.NAME, defineEnabled(eventType));
if (eventType.hasDuration()) { if (eventType.hasDuration()) {
eventControls.put(Threshold.NAME, defineThreshold(eventType)); addControl(Threshold.NAME, defineThreshold(eventType));
} }
if (eventType.hasStackTrace()) { if (eventType.hasStackTrace()) {
eventControls.put(StackTrace.NAME, defineStackTrace(eventType)); addControl(StackTrace.NAME, defineStackTrace(eventType));
} }
if (eventType.hasPeriod()) { if (eventType.hasPeriod()) {
eventControls.put(Period.NAME, definePeriod(eventType)); addControl(Period.NAME, definePeriod(eventType));
} }
if (eventType.hasCutoff()) { if (eventType.hasCutoff()) {
eventControls.put(Cutoff.NAME, defineCutoff(eventType)); addControl(Cutoff.NAME, defineCutoff(eventType));
} }
ArrayList<AnnotationElement> aes = new ArrayList<>(eventType.getAnnotationElements()); ArrayList<AnnotationElement> aes = new ArrayList<>(eventType.getAnnotationElements());
...@@ -99,6 +102,19 @@ public final class EventControl { ...@@ -99,6 +102,19 @@ public final class EventControl {
this.idName = String.valueOf(eventType.getId()); this.idName = String.valueOf(eventType.getId());
} }
private boolean hasControl(String name) {
for (NamedControl nc : namedControls) {
if (name.equals(nc.name)) {
return true;
}
}
return false;
}
private void addControl(String name, Control control) {
namedControls.add(new NamedControl(name, control));
}
static void remove(PlatformEventType type, List<AnnotationElement> aes, Class<? extends java.lang.annotation.Annotation> clazz) { static void remove(PlatformEventType type, List<AnnotationElement> aes, Class<? extends java.lang.annotation.Annotation> clazz) {
long id = Type.getTypeId(clazz); long id = Type.getTypeId(clazz);
for (AnnotationElement a : type.getAnnotationElements()) { for (AnnotationElement a : type.getAnnotationElements()) {
...@@ -131,7 +147,8 @@ public final class EventControl { ...@@ -131,7 +147,8 @@ public final class EventControl {
if (n != null) { if (n != null) {
name = n.value(); name = n.value();
} }
if (!eventControls.containsKey(name)) {
if (!hasControl(name)) {
defineSetting((Class<? extends SettingControl>) settingClass, m, type, name); defineSetting((Class<? extends SettingControl>) settingClass, m, type, name);
} }
} }
...@@ -161,7 +178,7 @@ public final class EventControl { ...@@ -161,7 +178,7 @@ public final class EventControl {
} }
} }
aes.trimToSize(); aes.trimToSize();
eventControls.put(settingName, si.settingControl); addControl(settingName, si.settingControl);
eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, settingName, defaultValue, aes)); eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, settingName, defaultValue, aes));
settingInfos.add(si); settingInfos.add(si);
} }
...@@ -245,9 +262,9 @@ public final class EventControl { ...@@ -245,9 +262,9 @@ public final class EventControl {
} }
void disable() { void disable() {
for (Control c : eventControls.values()) { for (NamedControl nc : namedControls) {
if (c instanceof EnabledSetting) { if (nc.control instanceof EnabledSetting) {
c.setValueSafe("false"); nc.control.setValueSafe("false");
return; return;
} }
} }
...@@ -257,24 +274,23 @@ public final class EventControl { ...@@ -257,24 +274,23 @@ public final class EventControl {
if (!type.isRegistered()) { if (!type.isRegistered()) {
return; return;
} }
for (Map.Entry<String, Control> entry : eventControls.entrySet()) { ActiveSettingEvent event = ActiveSettingEvent.EVENT.get();
Control c = entry.getValue(); for (NamedControl nc : namedControls) {
if (Utils.isSettingVisible(c, type.hasEventHook())) { if (Utils.isSettingVisible(nc.control, type.hasEventHook())) {
String value = c.getLastValue(); String value = nc.control.getLastValue();
if (value == null) { if (value == null) {
value = c.getDefaultValue(); value = nc.control.getDefaultValue();
} }
ActiveSettingEvent ase = new ActiveSettingEvent(); event.id = type.getId();
ase.id = type.getId(); event.name = nc.name;
ase.name = entry.getKey(); event.value = value;
ase.value = value; event.commit();
ase.commit();
} }
} }
} }
public Set<Entry<String, Control>> getEntries() { public ArrayList<NamedControl> getNamedControls() {
return eventControls.entrySet(); return namedControls;
} }
public PlatformEventType getEventType() { public PlatformEventType getEventType() {
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -355,74 +355,72 @@ public final class EventInstrumentation { ...@@ -355,74 +355,72 @@ public final class EventInstrumentation {
methodVisitor.visitMaxs(0, 0); methodVisitor.visitMaxs(0, 0);
}); });
// MyEvent#commit() - Java event writer
updateMethod(METHOD_COMMIT, methodVisitor -> { updateMethod(METHOD_COMMIT, methodVisitor -> {
// if (!isEnable()) { // if (!isEnable()) {
// return; // return;
// } // }
methodVisitor.visitCode(); methodVisitor.visitCode();
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
Label l0 = new Label(); Label l0 = new Label();
methodVisitor.visitJumpInsn(Opcodes.IFNE, l0); methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitLabel(l0); methodVisitor.visitLabel(l0);
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
// if (startTime == 0) { // if (startTime == 0) {
// startTime = EventWriter.timestamp(); // startTime = EventWriter.timestamp();
// } else { // } else {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J"); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
methodVisitor.visitInsn(Opcodes.LCONST_0); methodVisitor.visitInsn(Opcodes.LCONST_0);
methodVisitor.visitInsn(Opcodes.LCMP); methodVisitor.visitInsn(Opcodes.LCMP);
Label durationalEvent = new Label(); Label durationalEvent = new Label();
methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent); methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
METHOD_TIME_STAMP.getDescriptor(), false); methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J"); Label commit = new Label();
Label commit = new Label(); methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
methodVisitor.visitJumpInsn(Opcodes.GOTO, commit); // if (duration == 0) {
// if (duration == 0) { // duration = EventWriter.timestamp() - startTime;
// duration = EventWriter.timestamp() - startTime; // }
// } // }
// } methodVisitor.visitLabel(durationalEvent);
methodVisitor.visitLabel(durationalEvent); methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); methodVisitor.visitInsn(Opcodes.LCONST_0);
methodVisitor.visitInsn(Opcodes.LCONST_0); methodVisitor.visitInsn(Opcodes.LCMP);
methodVisitor.visitInsn(Opcodes.LCMP); methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
methodVisitor.visitJumpInsn(Opcodes.IFNE, commit); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J"); methodVisitor.visitInsn(Opcodes.LSUB);
methodVisitor.visitInsn(Opcodes.LSUB); methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J"); methodVisitor.visitLabel(commit);
methodVisitor.visitLabel(commit); // if (shouldCommit()) {
// if (shouldCommit()) { methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false); Label end = new Label();
Label end = new Label(); // eventHandler.write(...);
// eventHandler.write(...); // }
// } methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
methodVisitor.visitJumpInsn(Opcodes.IFEQ, end); methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
for (FieldInfo fi : fieldInfos) { for (FieldInfo fi : fieldInfos) {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
} }
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
methodVisitor.visitLabel(end); methodVisitor.visitLabel(end);
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitEnd(); methodVisitor.visitEnd();
}); });
// MyEvent#shouldCommit() // MyEvent#shouldCommit()
updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> { updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
package jdk.jfr.internal; package jdk.jfr.internal;
import sun.misc.Unsafe; import sun.misc.Unsafe;
import jdk.jfr.internal.consumer.RecordingInput; import jdk.jfr.internal.consumer.StringParser;
/** /**
* Class must reside in a package with package restriction. * Class must reside in a package with package restriction.
...@@ -115,18 +115,18 @@ public final class EventWriter { ...@@ -115,18 +115,18 @@ public final class EventWriter {
public void putString(String s, StringPool pool) { public void putString(String s, StringPool pool) {
if (s == null) { if (s == null) {
putByte(RecordingInput.STRING_ENCODING_NULL); putByte(StringParser.Encoding.NULL.byteValue());
return; return;
} }
int length = s.length(); int length = s.length();
if (length == 0) { if (length == 0) {
putByte(RecordingInput.STRING_ENCODING_EMPTY_STRING); putByte(StringParser.Encoding.EMPTY_STRING.byteValue());
return; return;
} }
if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) { if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) {
long l = StringPool.addString(s); long l = StringPool.addString(s);
if (l > 0) { if (l > 0) {
putByte(RecordingInput.STRING_ENCODING_CONSTANT_POOL); putByte(StringParser.Encoding.CONSTANT_POOL.byteValue());
putLong(l); putLong(l);
return; return;
} }
...@@ -138,7 +138,7 @@ public final class EventWriter { ...@@ -138,7 +138,7 @@ public final class EventWriter {
private void putStringValue(String s) { private void putStringValue(String s) {
int length = s.length(); int length = s.length();
if (isValidForSize(1 + 5 + 3 * length)) { if (isValidForSize(1 + 5 + 3 * length)) {
putUncheckedByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // 1 byte putUncheckedByte(StringParser.Encoding.CHAR_ARRAY.byteValue()); // 1 byte
putUncheckedInt(length); // max 5 bytes putUncheckedInt(length); // max 5 bytes
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
putUncheckedChar(s.charAt(i)); // max 3 bytes putUncheckedChar(s.charAt(i)); // max 3 bytes
...@@ -197,11 +197,7 @@ public final class EventWriter { ...@@ -197,11 +197,7 @@ public final class EventWriter {
if (currentPosition + requestedSize > maxPosition) { if (currentPosition + requestedSize > maxPosition) {
flushOnEnd = flush(usedSize(), requestedSize); flushOnEnd = flush(usedSize(), requestedSize);
// retry // retry
if (currentPosition + requestedSize > maxPosition) { if (!valid) {
Logger.log(LogTag.JFR_SYSTEM,
LogLevel.WARN, () ->
"Unable to commit. Requested size " + requestedSize + " too large");
valid = false;
return false; return false;
} }
} }
......
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
import jdk.jfr.internal.SecuritySupport.SafePath;
// This class keeps track of files that can't be deleted
// so they can a later staged be removed.
final class FilePurger {
private final static Set<SafePath> paths = new LinkedHashSet<>();
public synchronized static void add(SafePath p) {
paths.add(p);
if (paths.size() > 1000) {
removeOldest();
}
}
public synchronized static void purge() {
if (paths.isEmpty()) {
return;
}
for (SafePath p : new ArrayList<>(paths)) {
if (delete(p)) {
paths.remove(p);
}
}
}
private static void removeOldest() {
SafePath oldest = paths.iterator().next();
paths.remove(oldest);
}
private static boolean delete(SafePath p) {
try {
SecuritySupport.delete(p);
return true;
} catch (IOException e) {
return false;
}
}
}
/* /*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -54,7 +54,7 @@ public final class JVM { ...@@ -54,7 +54,7 @@ public final class JVM {
// subscribeLogLevel(tag, tag.id); // subscribeLogLevel(tag, tag.id);
// } // }
Options.ensureInitialized(); Options.ensureInitialized();
EventHandlerProxyCreator.ensureInitialized(); // EventHandlerProxyCreator.ensureInitialized();
} }
/** /**
...@@ -271,7 +271,6 @@ public final class JVM { ...@@ -271,7 +271,6 @@ public final class JVM {
* *
* @param file the file where data should be written, or null if it should * @param file the file where data should be written, or null if it should
* not be copied out (in memory). * not be copied out (in memory).
*
* @throws IOException * @throws IOException
*/ */
public native void setOutput(String file); public native void setOutput(String file);
...@@ -474,6 +473,16 @@ public final class JVM { ...@@ -474,6 +473,16 @@ public final class JVM {
*/ */
public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize); public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize);
/**
* Flushes all thread buffers to disk and the constant pool data needed to read
* them.
* <p>
* When the method returns, the chunk header should be updated with valid
* pointers to the metadata event, last check point event, correct file size and
* the generation id.
*
*/
public native void flush();
/** /**
* Sets the location of the disk repository, to be used at an emergency * Sets the location of the disk repository, to be used at an emergency
* dump. * dump.
...@@ -538,4 +547,30 @@ public final class JVM { ...@@ -538,4 +547,30 @@ public final class JVM {
* @return if it is time to perform a chunk rotation * @return if it is time to perform a chunk rotation
*/ */
public native boolean shouldRotateDisk(); public native boolean shouldRotateDisk();
/**
* Exclude a thread from the jfr system
*
*/
public native void exclude(Thread thread);
/**
* Include a thread back into the jfr system
*
*/
public native void include(Thread thread);
/**
* Test if a thread ius currently excluded from the jfr system.
*
* @return is thread currently excluded
*/
public native boolean isExcluded(Thread thread);
/**
* Get the start time in nanos from the header of the current chunk
*
*@return start time of the recording in nanos, -1 in case of in-memory
*/
public native long getChunkStartNanos();
} }
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -62,22 +62,26 @@ public enum LogTag { ...@@ -62,22 +62,26 @@ public enum LogTag {
* Covers metadata for JVM/JDK (for Hotspot developers) * Covers metadata for JVM/JDK (for Hotspot developers)
*/ */
JFR_SYSTEM_METADATA(6), JFR_SYSTEM_METADATA(6),
/**
* Covers streaming (for Hotspot developers)
*/
JFR_SYSTEM_STREAMING(7),
/** /**
* Covers metadata for Java user (for Hotspot developers) * Covers metadata for Java user (for Hotspot developers)
*/ */
JFR_METADATA(7), JFR_METADATA(8),
/** /**
* Covers events (for users of the JDK) * Covers events (for users of the JDK)
*/ */
JFR_EVENT(8), JFR_EVENT(9),
/** /**
* Covers setting (for users of the JDK) * Covers setting (for users of the JDK)
*/ */
JFR_SETTING(9), JFR_SETTING(10),
/** /**
* Covers usage of jcmd with JFR * Covers usage of jcmd with JFR
*/ */
JFR_DCMD(10); JFR_DCMD(11);
/* set from native side */ /* set from native side */
volatile int tagSetLevel = 100; // prevent logging if JVM log system has not been initialized volatile int tagSetLevel = 100; // prevent logging if JVM log system has not been initialized
......
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal;
import java.util.BitSet;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
@SuppressWarnings("unchecked")
public final class LongMap<T> {
private static final int MAXIMUM_CAPACITY = 1 << 30;
private static final long[] EMPTY_KEYS = new long[0];
private static final Object[] EMPTY_OBJECTS = new Object[0];
private static final int DEFAULT_SIZE = 32;
private static final Object NULL_OBJECT = new Object();
private final int bitCount;
private BitSet bitSet;
private long[] keys = EMPTY_KEYS;
private T[] objects = (T[]) EMPTY_OBJECTS;
private int count;
private int shift;
public LongMap() {
this.bitCount = 0;
}
public LongMap(int markBits) {
this.bitCount = markBits;
this.bitSet = new BitSet();
}
// Should be 2^n
private void initialize(int capacity) {
keys = new long[capacity];
objects = (T[]) new Object[capacity];
shift = 64 - (31 - Integer.numberOfLeadingZeros(capacity));
}
public void claimBits() {
// flip last bit back and forth to make bitset expand to max size
int lastBit = bitSetIndex(objects.length - 1, bitCount -1);
bitSet.flip(lastBit);
bitSet.flip(lastBit);
}
public void setId(long id, int bitIndex) {
int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
bitSet.set(bitSetIndex, true);
}
public void clearId(long id, int bitIndex) {
int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
bitSet.set(bitSetIndex, false);
}
public boolean isSetId(long id, int bitIndex) {
int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
return bitSet.get(bitSetIndex);
}
private int bitSetIndex(int tableIndex, int bitIndex) {
return bitCount * tableIndex + bitIndex;
}
private int tableIndexOf(long id) {
int index = index(id);
while (true) {
if (objects[index] == null) {
throw new InternalError("Unknown id");
}
if (keys[index] == id) {
return index;
}
index++;
if (index == keys.length) {
index = 0;
}
}
}
public boolean hasKey(long id) {
int index = index(id);
while (true) {
if (objects[index] == null) {
return false;
}
if (keys[index] == id) {
return true;
}
index++;
if (index == keys.length) {
index = 0;
}
}
}
public void expand(int size) {
int l = 4 * size / 3;
if (l <= keys.length) {
return;
}
int n = tableSizeFor(l);
LongMap<T> temp = new LongMap<>(bitCount);
temp.initialize(n);
// Optimization, avoid growing while copying bits
if (bitCount > 0 && !bitSet.isEmpty()) {
temp.claimBits();
claimBits();
}
for (int tIndex = 0; tIndex < keys.length; tIndex++) {
T o = objects[tIndex];
if (o != null) {
long key = keys[tIndex];
temp.put(key, o);
if (bitCount != 0) {
for (int bIndex = 0; bIndex < bitCount; bIndex++) {
boolean bitValue = isSetId(key, bIndex);
if (bitValue) {
temp.setId(key, bIndex);
}
}
}
}
}
keys = temp.keys;
objects = temp.objects;
shift = temp.shift;
bitSet = temp.bitSet;
}
public void put(long id, T object) {
if (keys == EMPTY_KEYS) {
// Lazy initialization
initialize(DEFAULT_SIZE);
}
if (object == null) {
object = (T) NULL_OBJECT;
}
int index = index(id);
// probe for empty slot
while (true) {
if (objects[index] == null) {
keys[index] = id;
objects[index] = object;
count++;
// Don't expand lazy since it
// can cause resize when replacing
// an object.
if (count > 3 * keys.length / 4) {
expand(2 * keys.length);
}
return;
}
// if it already exists, replace
if (keys[index] == id) {
objects[index] = object;
return;
}
index++;
if (index == keys.length) {
index = 0;
}
}
}
public T getAt(int tableIndex) {
T o = objects[tableIndex];
return o == NULL_OBJECT ? null : o;
}
public T get(long id) {
if (keys == EMPTY_KEYS) {
return null;
}
int index = index(id);
while (true) {
if (objects[index] == null) {
return null;
}
if (keys[index] == id) {
return getAt(index);
}
index++;
if (index == keys.length) {
index = 0;
}
}
}
private int index(long id) {
return (int) ((id * -7046029254386353131L) >>> shift);
}
// Copied from HashMap::tableSizeFor
private static final int tableSizeFor(int cap) {
int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
public void forEachKey(LongConsumer keyTraverser) {
for (int i = 0; i < keys.length; i++) {
if (objects[i] != null) {
keyTraverser.accept(keys[i]);
}
}
}
public void forEach(Consumer<T> consumer) {
for (int i = 0; i < keys.length; i++) {
T o = objects[i];
if (o != null) {
consumer.accept(o);
}
}
}
public int size() {
return count;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < objects.length; i++) {
sb.append(i);
sb.append(": id=");
sb.append(keys[i]);
sb.append(" ");
sb.append(objects[i]);
sb.append("\n");
}
return sb.toString();
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package jdk.jfr.internal; package jdk.jfr.internal;
import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -35,6 +34,7 @@ import java.util.Locale; ...@@ -35,6 +34,7 @@ import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import jdk.jfr.EventType; import jdk.jfr.EventType;
import jdk.jfr.internal.consumer.RecordingInput;
/** /**
* Metadata about a chunk * Metadata about a chunk
...@@ -214,6 +214,7 @@ public final class MetadataDescriptor { ...@@ -214,6 +214,7 @@ public final class MetadataDescriptor {
long gmtOffset; long gmtOffset;
String locale; String locale;
Element root; Element root;
public long metadataId;
// package private // package private
MetadataDescriptor() { MetadataDescriptor() {
...@@ -252,7 +253,7 @@ public final class MetadataDescriptor { ...@@ -252,7 +253,7 @@ public final class MetadataDescriptor {
return locale; return locale;
} }
public static MetadataDescriptor read(DataInput input) throws IOException { public static MetadataDescriptor read(RecordingInput input) throws IOException {
MetadataReader r = new MetadataReader(input); MetadataReader r = new MetadataReader(input);
return r.getDescriptor(); return r.getDescriptor();
} }
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -49,6 +49,8 @@ import jdk.jfr.AnnotationElement; ...@@ -49,6 +49,8 @@ import jdk.jfr.AnnotationElement;
import jdk.jfr.SettingDescriptor; import jdk.jfr.SettingDescriptor;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor.Element; import jdk.jfr.internal.MetadataDescriptor.Element;
import jdk.jfr.internal.consumer.RecordingInput;
import jdk.jfr.internal.consumer.StringParser;
/** /**
* Parses metadata. * Parses metadata.
...@@ -61,12 +63,13 @@ final class MetadataReader { ...@@ -61,12 +63,13 @@ final class MetadataReader {
private final MetadataDescriptor descriptor; private final MetadataDescriptor descriptor;
private final Map<Long, Type> types = new HashMap<>(); private final Map<Long, Type> types = new HashMap<>();
public MetadataReader(DataInput input) throws IOException { public MetadataReader(RecordingInput input) throws IOException {
this.input = input; this.input = input;
int size = input.readInt(); int size = input.readInt();
this.pool = new ArrayList<>(size); this.pool = new ArrayList<>(size);
StringParser p = new StringParser(null, false);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
this.pool.add(input.readUTF()); this.pool.add((String) p.parse(input));
} }
descriptor = new MetadataDescriptor(); descriptor = new MetadataDescriptor();
Element root = createElement(); Element root = createElement();
......
...@@ -45,6 +45,7 @@ import jdk.jfr.StackTrace; ...@@ -45,6 +45,7 @@ import jdk.jfr.StackTrace;
import jdk.jfr.Threshold; import jdk.jfr.Threshold;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.RequestEngine.RequestHook; import jdk.jfr.internal.RequestEngine.RequestHook;
import jdk.jfr.internal.consumer.RepositoryFiles;
import jdk.jfr.internal.handlers.EventHandler; import jdk.jfr.internal.handlers.EventHandler;
public final class MetadataRepository { public final class MetadataRepository {
...@@ -184,10 +185,14 @@ public final class MetadataRepository { ...@@ -184,10 +185,14 @@ public final class MetadataRepository {
} }
public synchronized List<EventControl> getEventControls() { public synchronized List<EventControl> getEventControls() {
List<EventControl> controls = new ArrayList<>(); List<Class<? extends Event>> eventClasses = jvm.getAllEventClasses();
ArrayList<EventControl> controls = new ArrayList<>(eventClasses.size() + nativeControls.size());
controls.addAll(nativeControls); controls.addAll(nativeControls);
for (EventHandler eh : getEventHandlers()) { for (Class<? extends Event> clazz : eventClasses) {
controls.add(eh.getEventControl()); EventHandler eh = Utils.getHandler(clazz);
if (eh != null) {
controls.add(eh.getEventControl());
}
} }
return controls; return controls;
} }
...@@ -240,7 +245,9 @@ public final class MetadataRepository { ...@@ -240,7 +245,9 @@ public final class MetadataRepository {
storeDescriptorInJVM(); storeDescriptorInJVM();
} }
jvm.setOutput(filename); jvm.setOutput(filename);
if (filename != null) {
RepositoryFiles.notifyNewFile();
}
unregisterUnloaded(); unregisterUnloaded();
if (unregistered) { if (unregistered) {
if (typeLibrary.clearUnregistered()) { if (typeLibrary.clearUnregistered()) {
...@@ -276,4 +283,11 @@ public final class MetadataRepository { ...@@ -276,4 +283,11 @@ public final class MetadataRepository {
unregistered = true; unregistered = true;
} }
public synchronized void flush() {
if (staleMetadata) {
storeDescriptorInJVM();
}
jvm.flush();
}
} }
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -53,7 +53,7 @@ import jdk.jfr.SettingDescriptor; ...@@ -53,7 +53,7 @@ import jdk.jfr.SettingDescriptor;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor.Attribute; import jdk.jfr.internal.MetadataDescriptor.Attribute;
import jdk.jfr.internal.MetadataDescriptor.Element; import jdk.jfr.internal.MetadataDescriptor.Element;
import jdk.jfr.internal.consumer.RecordingInput; import jdk.jfr.internal.consumer.StringParser;
/** /**
* Class responsible for converting a list of types into a format that can be * Class responsible for converting a list of types into a format that can be
...@@ -94,10 +94,10 @@ final class MetadataWriter { ...@@ -94,10 +94,10 @@ final class MetadataWriter {
private void writeString(DataOutput out, String s) throws IOException { private void writeString(DataOutput out, String s) throws IOException {
if (s == null ) { if (s == null ) {
out.writeByte(RecordingInput.STRING_ENCODING_NULL); out.writeByte(StringParser.Encoding.NULL.byteValue());
return; return;
} }
out.writeByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // encoding UTF-16 out.writeByte(StringParser.Encoding.CHAR_ARRAY.byteValue()); // encoding UTF-16
int length = s.length(); int length = s.length();
writeInt(out, length); writeInt(out, length);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
......
/* /*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -69,7 +69,7 @@ public final class PlatformEventType extends Type { ...@@ -69,7 +69,7 @@ public final class PlatformEventType extends Type {
super(name, Type.SUPER_TYPE_EVENT, id); super(name, Type.SUPER_TYPE_EVENT, id);
this.dynamicSettings = dynamicSettings; this.dynamicSettings = dynamicSettings;
this.isJVM = Type.isDefinedByJVM(id); this.isJVM = Type.isDefinedByJVM(id);
this.isMethodSampling = name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample"); this.isMethodSampling = isJVM && (name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample"));
this.isJDK = isJDK; this.isJDK = isJDK;
this.stackTraceOffset = stackTraceOffset(name, isJDK); this.stackTraceOffset = stackTraceOffset(name, isJDK);
} }
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -31,7 +31,6 @@ import static jdk.jfr.internal.LogLevel.WARN; ...@@ -31,7 +31,6 @@ import static jdk.jfr.internal.LogLevel.WARN;
import static jdk.jfr.internal.LogTag.JFR; import static jdk.jfr.internal.LogTag.JFR;
import static jdk.jfr.internal.LogTag.JFR_SYSTEM; import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
import java.io.IOException;
import java.security.AccessControlContext; import java.security.AccessControlContext;
import java.security.AccessController; import java.security.AccessController;
import java.time.Duration; import java.time.Duration;
...@@ -60,6 +59,7 @@ import jdk.jfr.internal.instrument.JDKEvents; ...@@ -60,6 +59,7 @@ import jdk.jfr.internal.instrument.JDKEvents;
public final class PlatformRecorder { public final class PlatformRecorder {
private final List<PlatformRecording> recordings = new ArrayList<>(); private final List<PlatformRecording> recordings = new ArrayList<>();
private final static List<SecureRecorderListener> changeListeners = new ArrayList<>(); private final static List<SecureRecorderListener> changeListeners = new ArrayList<>();
private final Repository repository; private final Repository repository;
...@@ -98,6 +98,7 @@ public final class PlatformRecorder { ...@@ -98,6 +98,7 @@ public final class PlatformRecorder {
Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> { Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> {
result.add(new Timer("JFR Recording Scheduler", true)); result.add(new Timer("JFR Recording Scheduler", true));
}); });
jvm.exclude(t);
t.start(); t.start();
t.join(); t.join();
return result.get(0); return result.get(0);
...@@ -206,7 +207,7 @@ public final class PlatformRecorder { ...@@ -206,7 +207,7 @@ public final class PlatformRecorder {
repository.clear(); repository.clear();
} }
synchronized void start(PlatformRecording recording) { synchronized long start(PlatformRecording recording) {
// State can only be NEW or DELAYED because of previous checks // State can only be NEW or DELAYED because of previous checks
Instant now = Instant.now(); Instant now = Instant.now();
recording.setStartTime(now); recording.setStartTime(now);
...@@ -217,14 +218,17 @@ public final class PlatformRecorder { ...@@ -217,14 +218,17 @@ public final class PlatformRecorder {
} }
boolean toDisk = recording.isToDisk(); boolean toDisk = recording.isToDisk();
boolean beginPhysical = true; boolean beginPhysical = true;
long streamInterval = recording.getStreamIntervalMillis();
for (PlatformRecording s : getRecordings()) { for (PlatformRecording s : getRecordings()) {
if (s.getState() == RecordingState.RUNNING) { if (s.getState() == RecordingState.RUNNING) {
beginPhysical = false; beginPhysical = false;
if (s.isToDisk()) { if (s.isToDisk()) {
toDisk = true; toDisk = true;
} }
streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
} }
} }
long startNanos = -1;
if (beginPhysical) { if (beginPhysical) {
RepositoryChunk newChunk = null; RepositoryChunk newChunk = null;
if (toDisk) { if (toDisk) {
...@@ -235,6 +239,7 @@ public final class PlatformRecorder { ...@@ -235,6 +239,7 @@ public final class PlatformRecorder {
} }
currentChunk = newChunk; currentChunk = newChunk;
jvm.beginRecording_(); jvm.beginRecording_();
startNanos = jvm.getChunkStartNanos();
recording.setState(RecordingState.RUNNING); recording.setState(RecordingState.RUNNING);
updateSettings(); updateSettings();
writeMetaEvents(); writeMetaEvents();
...@@ -244,6 +249,7 @@ public final class PlatformRecorder { ...@@ -244,6 +249,7 @@ public final class PlatformRecorder {
newChunk = repository.newChunk(now); newChunk = repository.newChunk(now);
RequestEngine.doChunkEnd(); RequestEngine.doChunkEnd();
MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
startNanos = jvm.getChunkStartNanos();
} }
recording.setState(RecordingState.RUNNING); recording.setState(RecordingState.RUNNING);
updateSettings(); updateSettings();
...@@ -253,8 +259,12 @@ public final class PlatformRecorder { ...@@ -253,8 +259,12 @@ public final class PlatformRecorder {
} }
currentChunk = newChunk; currentChunk = newChunk;
} }
if (toDisk) {
RequestEngine.setFlushInterval(streamInterval);
}
RequestEngine.doChunkBegin(); RequestEngine.doChunkBegin();
return startNanos;
} }
synchronized void stop(PlatformRecording recording) { synchronized void stop(PlatformRecording recording) {
...@@ -269,6 +279,7 @@ public final class PlatformRecorder { ...@@ -269,6 +279,7 @@ public final class PlatformRecorder {
Instant now = Instant.now(); Instant now = Instant.now();
boolean toDisk = false; boolean toDisk = false;
boolean endPhysical = true; boolean endPhysical = true;
long streamInterval = Long.MAX_VALUE;
for (PlatformRecording s : getRecordings()) { for (PlatformRecording s : getRecordings()) {
RecordingState rs = s.getState(); RecordingState rs = s.getState();
if (s != recording && RecordingState.RUNNING == rs) { if (s != recording && RecordingState.RUNNING == rs) {
...@@ -276,6 +287,7 @@ public final class PlatformRecorder { ...@@ -276,6 +287,7 @@ public final class PlatformRecorder {
if (s.isToDisk()) { if (s.isToDisk()) {
toDisk = true; toDisk = true;
} }
streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
} }
} }
OldObjectSample.emit(recording); OldObjectSample.emit(recording);
...@@ -311,6 +323,13 @@ public final class PlatformRecorder { ...@@ -311,6 +323,13 @@ public final class PlatformRecorder {
currentChunk = newChunk; currentChunk = newChunk;
RequestEngine.doChunkBegin(); RequestEngine.doChunkBegin();
} }
if (toDisk) {
RequestEngine.setFlushInterval(streamInterval);
} else {
RequestEngine.setFlushInterval(Long.MAX_VALUE);
}
recording.setState(RecordingState.STOPPED); recording.setState(RecordingState.STOPPED);
} }
...@@ -340,6 +359,18 @@ public final class PlatformRecorder { ...@@ -340,6 +359,18 @@ public final class PlatformRecorder {
MetadataRepository.getInstance().setSettings(list); MetadataRepository.getInstance().setSettings(list);
} }
public synchronized void rotateIfRecordingToDisk() {
boolean disk = false;
for (PlatformRecording s : getRecordings()) {
if (RecordingState.RUNNING == s.getState() && s.isToDisk()) {
disk = true;
}
}
if (disk) {
rotateDisk();
}
}
synchronized void rotateDisk() { synchronized void rotateDisk() {
Instant now = Instant.now(); Instant now = Instant.now();
RepositoryChunk newChunk = repository.newChunk(now); RepositoryChunk newChunk = repository.newChunk(now);
...@@ -397,14 +428,14 @@ public final class PlatformRecorder { ...@@ -397,14 +428,14 @@ public final class PlatformRecorder {
r.appendChunk(chunk); r.appendChunk(chunk);
} }
} }
FilePurger.purge();
} }
private void writeMetaEvents() { private void writeMetaEvents() {
if (activeRecordingEvent.isEnabled()) { if (activeRecordingEvent.isEnabled()) {
ActiveRecordingEvent event = ActiveRecordingEvent.EVENT.get();
for (PlatformRecording r : getRecordings()) { for (PlatformRecording r : getRecordings()) {
if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) { if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) {
ActiveRecordingEvent event = new ActiveRecordingEvent();
event.id = r.getId(); event.id = r.getId();
event.name = r.getName(); event.name = r.getName();
WriteableUserPath p = r.getDestination(); WriteableUserPath p = r.getDestination();
...@@ -417,6 +448,8 @@ public final class PlatformRecorder { ...@@ -417,6 +448,8 @@ public final class PlatformRecorder {
event.maxSize = size == null ? Long.MAX_VALUE : size; event.maxSize = size == null ? Long.MAX_VALUE : size;
Instant start = r.getStartTime(); Instant start = r.getStartTime();
event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli(); event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli();
Duration fi = r.getFlushInterval();
event.flushInterval = fi == null ? Long.MAX_VALUE : fi.toMillis();
event.commit(); event.commit();
} }
} }
...@@ -450,7 +483,7 @@ public final class PlatformRecorder { ...@@ -450,7 +483,7 @@ public final class PlatformRecorder {
JVM.FILE_DELTA_CHANGE.wait(duration < 10 ? 10 : duration); JVM.FILE_DELTA_CHANGE.wait(duration < 10 ? 10 : duration);
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); // Ignore
} }
} }
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -84,6 +84,7 @@ public final class PlatformRecording implements AutoCloseable { ...@@ -84,6 +84,7 @@ public final class PlatformRecording implements AutoCloseable {
private TimerTask startTask; private TimerTask startTask;
private AccessControlContext noDestinationDumpOnExitAccessControlContext; private AccessControlContext noDestinationDumpOnExitAccessControlContext;
private boolean shuoldWriteActiveRecordingEvent = true; private boolean shuoldWriteActiveRecordingEvent = true;
private Duration flushInterval = Duration.ofSeconds(1);
PlatformRecording(PlatformRecorder recorder, long id) { PlatformRecording(PlatformRecorder recorder, long id) {
// Typically the access control context is taken // Typically the access control context is taken
...@@ -98,9 +99,10 @@ public final class PlatformRecording implements AutoCloseable { ...@@ -98,9 +99,10 @@ public final class PlatformRecording implements AutoCloseable {
this.name = String.valueOf(id); this.name = String.valueOf(id);
} }
public void start() { public long start() {
RecordingState oldState; RecordingState oldState;
RecordingState newState; RecordingState newState;
long startNanos = -1;
synchronized (recorder) { synchronized (recorder) {
oldState = getState(); oldState = getState();
if (!Utils.isBefore(state, RecordingState.RUNNING)) { if (!Utils.isBefore(state, RecordingState.RUNNING)) {
...@@ -111,7 +113,7 @@ public final class PlatformRecording implements AutoCloseable { ...@@ -111,7 +113,7 @@ public final class PlatformRecording implements AutoCloseable {
startTask = null; startTask = null;
startTime = null; startTime = null;
} }
recorder.start(this); startNanos = recorder.start(this);
Logger.log(LogTag.JFR, LogLevel.INFO, () -> { Logger.log(LogTag.JFR, LogLevel.INFO, () -> {
// Only print non-default values so it easy to see // Only print non-default values so it easy to see
// which options were added // which options were added
...@@ -143,6 +145,8 @@ public final class PlatformRecording implements AutoCloseable { ...@@ -143,6 +145,8 @@ public final class PlatformRecording implements AutoCloseable {
newState = getState(); newState = getState();
} }
notifyIfStateChanged(oldState, newState); notifyIfStateChanged(oldState, newState);
return startNanos;
} }
public boolean stop(String reason) { public boolean stop(String reason) {
...@@ -779,4 +783,28 @@ public final class PlatformRecording implements AutoCloseable { ...@@ -779,4 +783,28 @@ public final class PlatformRecording implements AutoCloseable {
public boolean isRecorderEnabled(String eventName) { public boolean isRecorderEnabled(String eventName) {
return recorder.isEnabled(eventName); return recorder.isEnabled(eventName);
} }
public void setFlushInterval(Duration interval) {
synchronized (recorder) {
if (getState() == RecordingState.CLOSED) {
throw new IllegalStateException("Can't set stream interval when recording is closed");
}
this.flushInterval = interval;
}
}
public Duration getFlushInterval() {
synchronized (recorder) {
return flushInterval;
}
}
public long getStreamIntervalMillis() {
synchronized (recorder) {
if (flushInterval != null) {
return flushInterval.toMillis();
}
return Long.MAX_VALUE;
}
}
} }
/* /*
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -43,6 +43,7 @@ public final class Repository { ...@@ -43,6 +43,7 @@ public final class Repository {
public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter
.ofPattern("yyyy_MM_dd_HH_mm_ss"); .ofPattern("yyyy_MM_dd_HH_mm_ss");
private static final String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
private final Set<SafePath> cleanupDirectories = new HashSet<>(); private final Set<SafePath> cleanupDirectories = new HashSet<>();
private SafePath baseLocation; private SafePath baseLocation;
...@@ -55,7 +56,7 @@ public final class Repository { ...@@ -55,7 +56,7 @@ public final class Repository {
return instance; return instance;
} }
public synchronized void setBasePath(SafePath baseLocation) throws Exception { public synchronized void setBasePath(SafePath baseLocation) throws IOException {
// Probe to see if repository can be created, needed for fail fast // Probe to see if repository can be created, needed for fail fast
// during JVM startup or JFR.configure // during JVM startup or JFR.configure
this.repository = createRepository(baseLocation); this.repository = createRepository(baseLocation);
...@@ -69,7 +70,7 @@ public final class Repository { ...@@ -69,7 +70,7 @@ public final class Repository {
this.baseLocation = baseLocation; this.baseLocation = baseLocation;
} }
synchronized void ensureRepository() throws Exception { public synchronized void ensureRepository() throws IOException {
if (baseLocation == null) { if (baseLocation == null) {
setBasePath(SecuritySupport.JAVA_IO_TMPDIR); setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
} }
...@@ -91,7 +92,7 @@ public final class Repository { ...@@ -91,7 +92,7 @@ public final class Repository {
} }
} }
private static SafePath createRepository(SafePath basePath) throws Exception { private static SafePath createRepository(SafePath basePath) throws IOException {
SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath); SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
SafePath f = null; SafePath f = null;
...@@ -108,13 +109,14 @@ public final class Repository { ...@@ -108,13 +109,14 @@ public final class Repository {
} }
if (i == MAX_REPO_CREATION_RETRIES) { if (i == MAX_REPO_CREATION_RETRIES) {
throw new Exception("Unable to create JFR repository directory using base location (" + basePath + ")"); throw new IOException("Unable to create JFR repository directory using base location (" + basePath + ")");
} }
SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f); SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);
SecuritySupport.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, canonicalRepositoryPath.toString());
return canonicalRepositoryPath; return canonicalRepositoryPath;
} }
private static SafePath createRealBasePath(SafePath safePath) throws Exception { private static SafePath createRealBasePath(SafePath safePath) throws IOException {
if (SecuritySupport.exists(safePath)) { if (SecuritySupport.exists(safePath)) {
if (!SecuritySupport.isWritable(safePath)) { if (!SecuritySupport.isWritable(safePath)) {
throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable"); throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
...@@ -154,7 +156,7 @@ public final class Repository { ...@@ -154,7 +156,7 @@ public final class Repository {
SecuritySupport.clearDirectory(p); SecuritySupport.clearDirectory(p);
Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p); Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
} catch (IOException e) { } catch (IOException e) {
Logger.log(LogTag.JFR, LogLevel.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage()); Logger.log(LogTag.JFR, LogLevel.INFO, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
} }
} }
} }
......
/* /*
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -63,10 +63,10 @@ final class RepositoryChunk { ...@@ -63,10 +63,10 @@ final class RepositoryChunk {
LocalDateTime.ofInstant(startTime, z.getZone())); LocalDateTime.ofInstant(startTime, z.getZone()));
this.startTime = startTime; this.startTime = startTime;
this.repositoryPath = path; this.repositoryPath = path;
this.unFinishedFile = findFileName(repositoryPath, fileName, ".part"); this.unFinishedFile = findFileName(repositoryPath, fileName, ".jfr");
this.file = findFileName(repositoryPath, fileName, ".jfr"); this.file = findFileName(repositoryPath, fileName, ".jfr");
this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile); this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile);
SecuritySupport.touch(file); // SecuritySupport.touch(file);
} }
private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception { private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception {
...@@ -105,8 +105,6 @@ final class RepositoryChunk { ...@@ -105,8 +105,6 @@ final class RepositoryChunk {
private static long finish(SafePath unFinishedFile, SafePath file) throws IOException { private static long finish(SafePath unFinishedFile, SafePath file) throws IOException {
Objects.requireNonNull(unFinishedFile); Objects.requireNonNull(unFinishedFile);
Objects.requireNonNull(file); Objects.requireNonNull(file);
SecuritySupport.delete(file);
SecuritySupport.moveReplace(unFinishedFile, file);
return SecuritySupport.getFileSize(file); return SecuritySupport.getFileSize(file);
} }
...@@ -123,9 +121,11 @@ final class RepositoryChunk { ...@@ -123,9 +121,11 @@ final class RepositoryChunk {
SecuritySupport.delete(f); SecuritySupport.delete(f);
Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted"); Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted");
} catch (IOException e) { } catch (IOException e) {
Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage()); // Probably happens because file is being streamed
// on Windows where files in use can't be removed.
Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
if (f != null) { if (f != null) {
SecuritySupport.deleteOnExit(f); FilePurger.add(f);
} }
} }
} }
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -30,6 +30,7 @@ import java.security.AccessController; ...@@ -30,6 +30,7 @@ import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
...@@ -96,6 +97,8 @@ public final class RequestEngine { ...@@ -96,6 +97,8 @@ public final class RequestEngine {
private final static List<RequestHook> entries = new CopyOnWriteArrayList<>(); private final static List<RequestHook> entries = new CopyOnWriteArrayList<>();
private static long lastTimeMillis; private static long lastTimeMillis;
private static long flushInterval = Long.MAX_VALUE;
private static long streamDelta;
public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) { public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) {
Objects.requireNonNull(acc); Objects.requireNonNull(acc);
...@@ -209,7 +212,9 @@ public final class RequestEngine { ...@@ -209,7 +212,9 @@ public final class RequestEngine {
lastTimeMillis = now; lastTimeMillis = now;
return 0; return 0;
} }
for (RequestHook he : entries) { Iterator<RequestHook> hookIterator = entries.iterator();
while(hookIterator.hasNext()) {
RequestHook he = hookIterator.next();
long left = 0; long left = 0;
PlatformEventType es = he.type; PlatformEventType es = he.type;
// Not enabled, skip. // Not enabled, skip.
...@@ -228,7 +233,6 @@ public final class RequestEngine { ...@@ -228,7 +233,6 @@ public final class RequestEngine {
// for wait > period // for wait > period
r_delta = 0; r_delta = 0;
he.execute(); he.execute();
;
} }
// calculate time left // calculate time left
...@@ -250,7 +254,39 @@ public final class RequestEngine { ...@@ -250,7 +254,39 @@ public final class RequestEngine {
min = left; min = left;
} }
} }
// Flush should happen after all periodic events has been emitted
// Repeat of the above algorithm, but using the stream interval.
if (flushInterval != Long.MAX_VALUE) {
long r_period = flushInterval;
long r_delta = streamDelta;
r_delta += delta;
if (r_delta >= r_period) {
r_delta = 0;
MetadataRepository.getInstance().flush();
Utils.notifyFlush();
}
long left = (r_period - r_delta);
if (left < 0) {
left = 0;
}
streamDelta = r_delta;
if (min == 0 || left < min) {
min = left;
}
}
lastTimeMillis = now; lastTimeMillis = now;
return min; return min;
} }
static void setFlushInterval(long millis) {
// Don't accept shorter interval than 1 s.
long interval = millis < 1000 ? 1000 : millis;
flushInterval = interval;
if (interval < flushInterval) {
synchronized (JVM.FILE_DELTA_CHANGE) {
JVM.FILE_DELTA_CHANGE.notifyAll();
}
}
}
} }
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -37,6 +37,7 @@ import java.lang.reflect.Method; ...@@ -37,6 +37,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.ReflectPermission; import java.lang.reflect.ReflectPermission;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
...@@ -63,6 +64,7 @@ import jdk.jfr.FlightRecorder; ...@@ -63,6 +64,7 @@ import jdk.jfr.FlightRecorder;
import jdk.jfr.FlightRecorderListener; import jdk.jfr.FlightRecorderListener;
import jdk.jfr.FlightRecorderPermission; import jdk.jfr.FlightRecorderPermission;
import jdk.jfr.Recording; import jdk.jfr.Recording;
import jdk.jfr.internal.consumer.FileAccess;
/** /**
* Contains JFR code that does * Contains JFR code that does
...@@ -71,7 +73,7 @@ import jdk.jfr.Recording; ...@@ -71,7 +73,7 @@ import jdk.jfr.Recording;
public final class SecuritySupport { public final class SecuritySupport {
private final static Unsafe unsafe = Unsafe.getUnsafe(); private final static Unsafe unsafe = Unsafe.getUnsafe();
public final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr"); public final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr");
public final static FileAccess PRIVILIGED = new Privileged();
static final SafePath USER_HOME = getPathInProperty("user.home", null); static final SafePath USER_HOME = getPathInProperty("user.home", null);
static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null); static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null);
...@@ -138,7 +140,7 @@ public final class SecuritySupport { ...@@ -138,7 +140,7 @@ public final class SecuritySupport {
* a malicious provider. * a malicious provider.
* *
*/ */
public static final class SafePath { public static final class SafePath implements Comparable<SafePath> {
private final Path path; private final Path path;
private final String text; private final String text;
...@@ -156,9 +158,18 @@ public final class SecuritySupport { ...@@ -156,9 +158,18 @@ public final class SecuritySupport {
return path; return path;
} }
public File toFile() {
return path.toFile();
}
public String toString() { public String toString() {
return text; return text;
} }
@Override
public int compareTo(SafePath that) {
return that.text.compareTo(this.text);
}
} }
private interface RunnableWithCheckedException { private interface RunnableWithCheckedException {
...@@ -256,6 +267,10 @@ public final class SecuritySupport { ...@@ -256,6 +267,10 @@ public final class SecuritySupport {
doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
} }
public static void setProperty(String propertyName, String value) {
doPrivileged(() -> System.setProperty(propertyName, value), new PropertyPermission(propertyName, "write"));
}
static boolean getBooleanProperty(String propertyName) { static boolean getBooleanProperty(String propertyName) {
return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read")); return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read"));
} }
...@@ -296,7 +311,7 @@ public final class SecuritySupport { ...@@ -296,7 +311,7 @@ public final class SecuritySupport {
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner())); doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
} }
static SafePath toRealPath(SafePath safePath) throws Exception { static SafePath toRealPath(SafePath safePath) throws IOException {
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath())); return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath()));
} }
...@@ -322,7 +337,8 @@ public final class SecuritySupport { ...@@ -322,7 +337,8 @@ public final class SecuritySupport {
} }
public static boolean exists(SafePath safePath) throws IOException { public static boolean exists(SafePath safePath) throws IOException {
return doPrivilegedIOWithReturn(() -> Files.exists(safePath.toPath())); // Files.exist(path) is allocation intensive
return doPrivilegedIOWithReturn(() -> safePath.toPath().toFile().exists());
} }
public static boolean isDirectory(SafePath safePath) throws IOException { public static boolean isDirectory(SafePath safePath) throws IOException {
...@@ -377,7 +393,7 @@ public final class SecuritySupport { ...@@ -377,7 +393,7 @@ public final class SecuritySupport {
return unsafe.defineClass(name, bytes, 0, bytes.length, classLoader, null); return unsafe.defineClass(name, bytes, 0, bytes.length, classLoader, null);
} }
static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) { public static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]); return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]);
} }
...@@ -388,4 +404,32 @@ public final class SecuritySupport { ...@@ -388,4 +404,32 @@ public final class SecuritySupport {
public static SafePath getAbsolutePath(SafePath path) throws IOException { public static SafePath getAbsolutePath(SafePath path) throws IOException {
return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath()))); return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath())));
} }
private final static class Privileged extends FileAccess {
@Override
public RandomAccessFile openRAF(File f, String mode) throws IOException {
return doPrivilegedIOWithReturn( () -> new RandomAccessFile(f, mode));
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path directory) throws IOException {
return doPrivilegedIOWithReturn( () -> Files.newDirectoryStream(directory));
}
@Override
public String getAbsolutePath(File f) throws IOException {
return doPrivilegedIOWithReturn( () -> f.getAbsolutePath());
}
@Override
public long length(File f) throws IOException {
return doPrivilegedIOWithReturn( () -> f.length());
}
@Override
public long fileSize(Path p) throws IOException {
return doPrivilegedIOWithReturn( () -> Files.size(p));
}
}
} }
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -33,11 +33,11 @@ import java.util.HashSet; ...@@ -33,11 +33,11 @@ import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.StringJoiner; import java.util.StringJoiner;
import jdk.jfr.Event; import jdk.jfr.Event;
import jdk.jfr.internal.EventControl.NamedControl;
import jdk.jfr.internal.handlers.EventHandler; import jdk.jfr.internal.handlers.EventHandler;
final class SettingsManager { final class SettingsManager {
...@@ -214,18 +214,21 @@ final class SettingsManager { ...@@ -214,18 +214,21 @@ final class SettingsManager {
void setEventControl(EventControl ec) { void setEventControl(EventControl ec) {
InternalSetting is = getInternalSetting(ec); InternalSetting is = getInternalSetting(ec);
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {"); boolean shouldLog = Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO);
for (Entry<String, Control> entry : ec.getEntries()) { if (shouldLog) {
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {");
}
for (NamedControl nc: ec.getNamedControls()) {
Set<String> values = null; Set<String> values = null;
String settingName = entry.getKey(); String settingName = nc.name;
if (is != null) { if (is != null) {
values = is.getValues(settingName); values = is.getValues(settingName);
} }
Control control = entry.getValue(); Control control = nc.control;
if (values != null) { if (values != null) {
control.apply(values); control.apply(values);
String after = control.getLastValue(); String after = control.getLastValue();
if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) { if (shouldLog) {
if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) { if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) {
if (values.size() > 1) { if (values.size() > 1) {
StringJoiner sj = new StringJoiner(", ", "{", "}"); StringJoiner sj = new StringJoiner(", ", "{", "}");
...@@ -242,14 +245,16 @@ final class SettingsManager { ...@@ -242,14 +245,16 @@ final class SettingsManager {
} }
} else { } else {
control.setDefault(); control.setDefault();
if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) { if (shouldLog) {
String message = " " + settingName + "=\"" + control.getLastValue() + "\""; String message = " " + settingName + "=\"" + control.getLastValue() + "\"";
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message);
} }
} }
} }
ec.writeActiveSettingEvent(); ec.writeActiveSettingEvent();
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}"); if (shouldLog) {
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}");
}
} }
private InternalSetting getInternalSetting(EventControl ec) { private InternalSetting getInternalSetting(EventControl ec) {
......
...@@ -408,6 +408,7 @@ public final class TypeLibrary { ...@@ -408,6 +408,7 @@ public final class TypeLibrary {
// Purpose of this method is to mark types that are reachable // Purpose of this method is to mark types that are reachable
// from registered event types. Those types that are not reachable can // from registered event types. Those types that are not reachable can
// safely be removed // safely be removed
// Returns true if type was removed
public boolean clearUnregistered() { public boolean clearUnregistered() {
Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Cleaning out obsolete metadata"); Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Cleaning out obsolete metadata");
List<Type> registered = new ArrayList<>(); List<Type> registered = new ArrayList<>();
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -43,6 +43,7 @@ import java.lang.reflect.Method; ...@@ -43,6 +43,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -65,18 +66,18 @@ import jdk.jfr.internal.settings.ThresholdSetting; ...@@ -65,18 +66,18 @@ import jdk.jfr.internal.settings.ThresholdSetting;
public final class Utils { public final class Utils {
private static final Object flushObject = new Object();
private static final String INFINITY = "infinity"; private static final String INFINITY = "infinity";
private static Boolean SAVE_GENERATED;
public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events"; public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events";
public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument"; public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument";
public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers"; public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers";
public static final String REGISTER_EVENT = "registerEvent"; public static final String REGISTER_EVENT = "registerEvent";
public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder"; public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder";
private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk."; private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
private static Boolean SAVE_GENERATED;
public static void checkAccessFlightRecorder() throws SecurityException { public static void checkAccessFlightRecorder() throws SecurityException {
SecurityManager sm = System.getSecurityManager(); SecurityManager sm = System.getSecurityManager();
if (sm != null) { if (sm != null) {
...@@ -551,4 +552,33 @@ public final class Utils { ...@@ -551,4 +552,33 @@ public final class Utils {
String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId()); String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId());
return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr"; return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr";
} }
public static void takeNap(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// ok
}
}
public static void notifyFlush() {
synchronized (flushObject) {
flushObject.notifyAll();
}
}
public static void waitFlush(long timeOut) {
synchronized (flushObject) {
try {
flushObject.wait(timeOut);
} catch (InterruptedException e) {
// OK
}
}
}
public static long timeToNanos(Instant timestamp) {
return timestamp.getEpochSecond() * 1_000_000_000L + timestamp.getNano();
}
} }
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.SecuritySupport;
/*
* Purpose of this class is to simplify the implementation of
* an event stream.
*/
abstract class AbstractEventStream implements EventStream {
private final static AtomicLong counter = new AtomicLong(1);
private final Object terminated = new Object();
private final boolean active;
private final Runnable flushOperation = () -> dispatcher().runFlushActions();
private final AccessControlContext accessControllerContext;
private final StreamConfiguration configuration = new StreamConfiguration();
private volatile Thread thread;
private Dispatcher dispatcher;
private volatile boolean closed;
AbstractEventStream(AccessControlContext acc, boolean active) throws IOException {
this.accessControllerContext = Objects.requireNonNull(acc);
this.active = active;
}
@Override
abstract public void start();
@Override
abstract public void startAsync();
@Override
abstract public void close();
protected final Dispatcher dispatcher() {
if (configuration.hasChanged()) {
synchronized (configuration) {
dispatcher = new Dispatcher(configuration);
}
}
return dispatcher;
}
@Override
public final void setOrdered(boolean ordered) {
configuration.setOrdered(ordered);
}
@Override
public final void setReuse(boolean reuse) {
configuration.setReuse(reuse);
}
@Override
public final void setStartTime(Instant startTime) {
Objects.nonNull(startTime);
synchronized (configuration) {
if (configuration.started) {
throw new IllegalStateException("Stream is already started");
}
if (startTime.isBefore(Instant.EPOCH)) {
startTime = Instant.EPOCH;
}
configuration.setStartTime(startTime);
}
}
@Override
public final void setEndTime(Instant endTime) {
Objects.requireNonNull(endTime);
synchronized (configuration) {
if (configuration.started) {
throw new IllegalStateException("Stream is already started");
}
configuration.setEndTime(endTime);
}
}
@Override
public final void onEvent(Consumer<RecordedEvent> action) {
Objects.requireNonNull(action);
configuration.addEventAction(action);
}
@Override
public final void onEvent(String eventName, Consumer<RecordedEvent> action) {
Objects.requireNonNull(eventName);
Objects.requireNonNull(action);
configuration.addEventAction(eventName, action);
}
@Override
public final void onFlush(Runnable action) {
Objects.requireNonNull(action);
configuration.addFlushAction(action);
}
@Override
public final void onClose(Runnable action) {
Objects.requireNonNull(action);
configuration.addCloseAction(action);
}
@Override
public final void onError(Consumer<Throwable> action) {
Objects.requireNonNull(action);
configuration.addErrorAction(action);
}
@Override
public final boolean remove(Object action) {
Objects.requireNonNull(action);
return configuration.remove(action);
}
@Override
public final void awaitTermination() throws InterruptedException {
awaitTermination(Duration.ofMillis(0));
}
@Override
public final void awaitTermination(Duration timeout) throws InterruptedException {
Objects.requireNonNull(timeout);
if (timeout.isNegative()) {
throw new IllegalArgumentException("timeout value is negative");
}
long base = System.currentTimeMillis();
long now = 0;
long millis;
try {
millis = Math.multiplyExact(timeout.getSeconds(), 1000);
} catch (ArithmeticException a) {
millis = Long.MAX_VALUE;
}
int nanos = timeout.getNano();
if (nanos == 0 && millis == 0) {
synchronized (terminated) {
while (!isClosed()) {
terminated.wait(0);
}
}
} else {
while (!isClosed()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
synchronized (terminated) {
terminated.wait(delay, nanos);
}
now = System.currentTimeMillis() - base;
}
}
}
protected abstract void process() throws IOException;
protected final void setClosed(boolean closed) {
this.closed = closed;
}
protected final boolean isClosed() {
return closed;
}
public final void startAsync(long startNanos) {
startInternal(startNanos);
Runnable r = () -> run(accessControllerContext);
thread = SecuritySupport.createThreadWitNoPermissions(nextThreadName(), r);
thread.start();
}
public final void start(long startNanos) {
startInternal(startNanos);
thread = Thread.currentThread();
run(accessControllerContext);
}
protected final Runnable getFlushOperation() {
return flushOperation;
}
private void startInternal(long startNanos) {
synchronized (configuration) {
if (configuration.started) {
throw new IllegalStateException("Event stream can only be started once");
}
if (active && configuration.startTime == null) {
configuration.setStartNanos(startNanos);
}
configuration.setStarted(true);
}
}
private void execute() {
try {
process();
} catch (IOException ioe) {
// This can happen if a chunk file is removed, or
// a file is access that has been closed
// This is "normal" behavior for streaming and the
// stream will be closed when this happens.
} finally {
Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "Execution of stream ended.");
try {
close();
} finally {
synchronized (terminated) {
terminated.notifyAll();
}
}
}
}
private void run(AccessControlContext accessControlContext) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
execute();
return null;
}
}, accessControlContext);
}
private String nextThreadName() {
counter.incrementAndGet();
return "JFR Event Stream " + counter;
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,48 +25,60 @@ ...@@ -25,48 +25,60 @@
package jdk.jfr.internal.consumer; package jdk.jfr.internal.consumer;
import java.io.DataInput;
import java.io.IOException; import java.io.IOException;
import jdk.jfr.internal.LogLevel; import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag; import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger; import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MetadataDescriptor; import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Utils;
public final class ChunkHeader { public final class ChunkHeader {
private static final long HEADER_SIZE = 68;
private static final byte UPDATING_CHUNK_HEADER = (byte) 255;
private static final long CHUNK_SIZE_POSITION = 8;
private static final long DURATION_NANOS_POSITION = 40;
private static final long FILE_STATE_POSITION = 64;
private static final long METADATA_TYPE_ID = 0; private static final long METADATA_TYPE_ID = 0;
private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' }; private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' };
private final short major; private final short major;
private final short minor; private final short minor;
private final long chunkSize;
private final long chunkStartTicks; private final long chunkStartTicks;
private final long ticksPerSecond; private final long ticksPerSecond;
private final long chunkStartNanos; private final long chunkStartNanos;
private final long metadataPosition;
// private final long absoluteInitialConstantPoolPosition;
private final long absoluteChunkEnd;
private final long absoluteEventStart;
private final long absoluteChunkStart; private final long absoluteChunkStart;
private final boolean lastChunk;
private final RecordingInput input; private final RecordingInput input;
private final long durationNanos;
private final long id; private final long id;
private long constantPoolPosition; private long absoluteEventStart;
private long chunkSize = 0;
private long constantPoolPosition = 0;
private long metadataPosition = 0;
private long durationNanos;
private long absoluteChunkEnd;
private boolean isFinished;
private boolean finished;
public ChunkHeader(RecordingInput input) throws IOException { public ChunkHeader(RecordingInput input) throws IOException {
this(input, 0, 0); this(input, 0, 0);
} }
private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException { private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException {
this.absoluteChunkStart = absoluteChunkStart;
this.absoluteEventStart = absoluteChunkStart + HEADER_SIZE;
if (input.getFileSize() < HEADER_SIZE) {
throw new IOException("Not a complete Chunk header");
}
input.setValidSize(absoluteChunkStart + HEADER_SIZE);
input.position(absoluteChunkStart); input.position(absoluteChunkStart);
if (input.position() >= input.size()) { if (input.position() >= input.size()) {
throw new IOException("Chunk contains no data"); throw new IOException("Chunk contains no data");
} }
verifyMagic(input); verifyMagic(input);
this.input = input; this.input = input;
this.id = id; this.id = id;
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: " + id);
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: file=" + input.getFilename());
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart);
major = input.readRawShort(); major = input.readRawShort();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major);
...@@ -75,11 +87,11 @@ public final class ChunkHeader { ...@@ -75,11 +87,11 @@ public final class ChunkHeader {
if (major != 1 && major != 2) { if (major != 1 && major != 2) {
throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK."); throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK.");
} }
chunkSize = input.readRawLong(); input.readRawLong(); // chunk size
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
this.constantPoolPosition = input.readRawLong(); input.readRawLong(); // constant pool position
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
metadataPosition = input.readRawLong(); input.readRawLong(); // metadata position
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
chunkStartNanos = input.readRawLong(); // nanos since epoch chunkStartNanos = input.readRawLong(); // nanos since epoch
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);
...@@ -91,21 +103,98 @@ public final class ChunkHeader { ...@@ -91,21 +103,98 @@ public final class ChunkHeader {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond);
input.readRawInt(); // features, not used input.readRawInt(); // features, not used
// set up boundaries refresh();
this.absoluteChunkStart = absoluteChunkStart;
absoluteChunkEnd = absoluteChunkStart + chunkSize;
lastChunk = input.size() == absoluteChunkEnd;
absoluteEventStart = input.position();
// read metadata
input.position(absoluteEventStart); input.position(absoluteEventStart);
} }
void refresh() throws IOException {
while (true) {
byte fileState1;
input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) {
Utils.takeNap(1);
input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
}
input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION);
long chunkSize = input.readPhysicalLong();
long constantPoolPosition = input.readPhysicalLong();
long metadataPosition = input.readPhysicalLong();
input.positionPhysical(absoluteChunkStart + DURATION_NANOS_POSITION);
long durationNanos = input.readPhysicalLong();
input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
byte fileState2 = input.readPhysicalByte();
if (fileState1 == fileState2) { // valid header
finished = fileState1 == 0;
if (metadataPosition != 0) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Setting input size to " + (absoluteChunkStart + chunkSize));
if (finished) {
// This assumes that the whole recording
// is finished if the first chunk is.
// This is a limitation we may want to
// remove, but greatly improves performance as
// data can be read across chunk boundaries
// of multi-chunk files and only once.
input.setValidSize(input.getFileSize());
} else {
input.setValidSize(absoluteChunkStart + chunkSize);
}
this.chunkSize = chunkSize;
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
this.constantPoolPosition = constantPoolPosition;
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
this.metadataPosition = metadataPosition;
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
this.durationNanos = durationNanos;
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos =" + durationNanos);
isFinished = fileState2 == 0;
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: generation=" + fileState2);
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: finished=" + isFinished);
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: fileSize=" + input.size());
absoluteChunkEnd = absoluteChunkStart + chunkSize;
return;
}
}
}
}
public void awaitFinished() throws IOException {
if (finished) {
return;
}
long pos = input.position();
try {
input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
while (true) {
byte filestate = input.readPhysicalByte();
if (filestate == 0) {
finished = true;
return;
}
Utils.takeNap(1);
}
} finally {
input.position(pos);
}
}
public boolean isLastChunk() throws IOException {
awaitFinished();
// streaming files only have one chunk
return input.getFileSize() == absoluteChunkEnd;
}
public boolean isFinished() throws IOException {
return isFinished;
}
public ChunkHeader nextHeader() throws IOException { public ChunkHeader nextHeader() throws IOException {
return new ChunkHeader(input, absoluteChunkEnd, id + 1); return new ChunkHeader(input, absoluteChunkEnd, id + 1);
} }
public MetadataDescriptor readMetadata() throws IOException { public MetadataDescriptor readMetadata() throws IOException {
return readMetadata(null);
}
public MetadataDescriptor readMetadata(MetadataDescriptor previous) throws IOException {
input.position(absoluteChunkStart + metadataPosition); input.position(absoluteChunkStart + metadataPosition);
input.readInt(); // size input.readInt(); // size
long id = input.readLong(); // event type id long id = input.readLong(); // event type id
...@@ -115,15 +204,15 @@ public final class ChunkHeader { ...@@ -115,15 +204,15 @@ public final class ChunkHeader {
input.readLong(); // start time input.readLong(); // start time
input.readLong(); // duration input.readLong(); // duration
long metadataId = input.readLong(); long metadataId = input.readLong();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId); if (previous != null && metadataId == previous.metadataId) {
// No need to read if metadataId == lastMetadataId, but we return previous;
// do it for verification purposes. }
return MetadataDescriptor.read(input); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "New metadata id = " + metadataId);
MetadataDescriptor m = MetadataDescriptor.read(input);
m.metadataId = metadataId;
return m;
} }
public boolean isLastChunk() {
return lastChunk;
}
public short getMajor() { public short getMajor() {
return major; return major;
...@@ -137,13 +226,22 @@ public final class ChunkHeader { ...@@ -137,13 +226,22 @@ public final class ChunkHeader {
return absoluteChunkStart; return absoluteChunkStart;
} }
public long getAbsoluteEventStart() {
return absoluteEventStart;
}
public long getConstantPoolPosition() { public long getConstantPoolPosition() {
return constantPoolPosition; return constantPoolPosition;
} }
public long getMetataPosition() {
return metadataPosition;
}
public long getStartTicks() { public long getStartTicks() {
return chunkStartTicks; return chunkStartTicks;
} }
public long getChunkSize() {
return chunkSize;
}
public double getTicksPerSecond() { public double getTicksPerSecond() {
return ticksPerSecond; return ticksPerSecond;
...@@ -169,7 +267,7 @@ public final class ChunkHeader { ...@@ -169,7 +267,7 @@ public final class ChunkHeader {
return input; return input;
} }
private static void verifyMagic(DataInput input) throws IOException { private static void verifyMagic(RecordingInput input) throws IOException {
for (byte c : FILE_MAGIC) { for (byte c : FILE_MAGIC) {
if (input.readByte() != c) { if (input.readByte() != c) {
throw new IOException("Not a Flight Recorder file"); throw new IOException("Not a Flight Recorder file");
...@@ -181,4 +279,7 @@ public final class ChunkHeader { ...@@ -181,4 +279,7 @@ public final class ChunkHeader {
return absoluteEventStart; return absoluteEventStart;
} }
static long headerSize() {
return HEADER_SIZE;
}
} }
/*
* Copyright (c) 2016, 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.StringJoiner;
import jdk.jfr.EventType;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.LongMap;
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.Utils;
/**
* Parses a chunk.
*
*/
public final class ChunkParser {
static final class ParserConfiguration {
private final boolean reuse;
private final boolean ordered;
private final ParserFilter eventFilter;
long filterStart;
long filterEnd;
ParserConfiguration(long filterStart, long filterEnd, boolean reuse, boolean ordered, ParserFilter filter) {
this.filterStart = filterStart;
this.filterEnd = filterEnd;
this.reuse = reuse;
this.ordered = ordered;
this.eventFilter = filter;
}
public ParserConfiguration() {
this(0, Long.MAX_VALUE, false, false, ParserFilter.ACCEPT_ALL);
}
public boolean isOrdered() {
return ordered;
}
}
private enum CheckPointType {
// Checkpoint that finishes a flush segment
FLUSH(1),
// Checkpoint contains chunk header information in the first pool
CHUNK_HEADER(2),
// Checkpoint contains only statics that will not change from chunk to chunk
STATICS(4),
// Checkpoint contains thread related information
THREAD(8);
private final int mask;
private CheckPointType(int mask) {
this.mask = mask;
}
private boolean is(int flags) {
return (mask & flags) != 0;
}
}
private static final long CONSTANT_POOL_TYPE_ID = 1;
private static final String CHUNKHEADER = "jdk.types.ChunkHeader";
private final RecordingInput input;
private final ChunkHeader chunkHeader;
private final MetadataDescriptor metadata;
private final TimeConverter timeConverter;
private final MetadataDescriptor previousMetadata;
private final LongMap<ConstantLookup> constantLookups;
private LongMap<Type> typeMap;
private LongMap<Parser> parsers;
private boolean chunkFinished;
private Runnable flushOperation;
private ParserConfiguration configuration;
public ChunkParser(RecordingInput input) throws IOException {
this(input, new ParserConfiguration());
}
ChunkParser(RecordingInput input, ParserConfiguration pc) throws IOException {
this(new ChunkHeader(input), null, pc);
}
private ChunkParser(ChunkParser previous) throws IOException {
this(new ChunkHeader(previous.input), previous, new ParserConfiguration());
}
private ChunkParser(ChunkHeader header, ChunkParser previous, ParserConfiguration pc) throws IOException {
this.configuration = pc;
this.input = header.getInput();
this.chunkHeader = header;
if (previous == null) {
this.constantLookups = new LongMap<>();
this.previousMetadata = null;
} else {
this.constantLookups = previous.constantLookups;
this.previousMetadata = previous.metadata;
this.configuration = previous.configuration;
}
this.metadata = header.readMetadata(previousMetadata);
this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
if (metadata != previousMetadata) {
ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
parsers = factory.getParsers();
typeMap = factory.getTypeMap();
updateConfiguration();
} else {
parsers = previous.parsers;
typeMap = previous.typeMap;
}
constantLookups.forEach(c -> c.newPool());
fillConstantPools(0);
constantLookups.forEach(c -> c.getLatestPool().setResolving());
constantLookups.forEach(c -> c.getLatestPool().resolve());
constantLookups.forEach(c -> c.getLatestPool().setResolved());
input.position(chunkHeader.getEventStart());
}
public ChunkParser nextChunkParser() throws IOException {
return new ChunkParser(chunkHeader.nextHeader(), this, configuration);
}
private void updateConfiguration() {
updateConfiguration(configuration, false);
}
void updateConfiguration(ParserConfiguration configuration, boolean resetEventCache) {
this.configuration = configuration;
parsers.forEach(p -> {
if (p instanceof EventParser) {
EventParser ep = (EventParser) p;
if (resetEventCache) {
ep.resetCache();
}
String name = ep.getEventType().getName();
ep.setOrdered(configuration.ordered);
ep.setReuse(configuration.reuse);
ep.setFilterStart(configuration.filterStart);
ep.setFilterEnd(configuration.filterEnd);
long threshold = configuration.eventFilter.getThreshold(name);
if (threshold >= 0) {
ep.setEnabled(true);
ep.setThresholdNanos(threshold);
} else {
ep.setEnabled(false);
ep.setThresholdNanos(Long.MAX_VALUE);
}
}
});
}
/**
* Reads an event and returns null when segment or chunk ends.
*
* @param awaitNewEvents wait for new data.
*/
RecordedEvent readStreamingEvent(boolean awaitNewEvents) throws IOException {
long absoluteChunkEnd = chunkHeader.getEnd();
while (true) {
RecordedEvent event = readEvent();
if (event != null) {
return event;
}
if (!awaitNewEvents) {
return null;
}
long lastValid = absoluteChunkEnd;
long metadataPoistion = chunkHeader.getMetataPosition();
long contantPosition = chunkHeader.getConstantPoolPosition();
chunkFinished = awaitUpdatedHeader(absoluteChunkEnd);
if (chunkFinished) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "At chunk end");
return null;
}
absoluteChunkEnd = chunkHeader.getEnd();
// Read metadata and constant pools for the next segment
if (chunkHeader.getMetataPosition() != metadataPoistion) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new metadata in chunk. Rebuilding types and parsers");
MetadataDescriptor metadata = chunkHeader.readMetadata(previousMetadata);
ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
parsers = factory.getParsers();
typeMap = factory.getTypeMap();
updateConfiguration();;
}
if (contantPosition != chunkHeader.getConstantPoolPosition()) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new constant pool data. Filling up pools with new values");
constantLookups.forEach(c -> c.getLatestPool().setAllResolved(false));
fillConstantPools(contantPosition + chunkHeader.getAbsoluteChunkStart());
constantLookups.forEach(c -> c.getLatestPool().setResolving());
constantLookups.forEach(c -> c.getLatestPool().resolve());
constantLookups.forEach(c -> c.getLatestPool().setResolved());
}
input.position(lastValid);
}
}
/**
* Reads an event and returns null when the chunk ends
*/
public RecordedEvent readEvent() throws IOException {
long absoluteChunkEnd = chunkHeader.getEnd();
while (input.position() < absoluteChunkEnd) {
long pos = input.position();
int size = input.readInt();
if (size == 0) {
throw new IOException("Event can't have zero size");
}
long typeId = input.readLong();
Parser p = parsers.get(typeId);
if (p instanceof EventParser) {
// Fast path
EventParser ep = (EventParser) p;
RecordedEvent event = ep.parse(input);
if (event != null) {
input.position(pos + size);
return event;
}
// Not accepted by filter
} else {
if (typeId == 1) { // checkpoint event
if (flushOperation != null) {
parseCheckpoint();
}
} else {
if (typeId != 0) { // Not metadata event
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Unknown event type " + typeId);
}
}
}
input.position(pos + size);
}
return null;
}
private void parseCheckpoint() throws IOException {
// Content has been parsed previously. This
// is to trigger flush
input.readLong(); // timestamp
input.readLong(); // duration
input.readLong(); // delta
byte typeFlags = input.readByte();
if (CheckPointType.FLUSH.is(typeFlags)) {
flushOperation.run();
}
}
private boolean awaitUpdatedHeader(long absoluteChunkEnd) throws IOException {
if (Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO)) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Waiting for more data (streaming). Read so far: " + chunkHeader.getChunkSize() + " bytes");
}
while (true) {
chunkHeader.refresh();
if (absoluteChunkEnd != chunkHeader.getEnd()) {
return false;
}
if (chunkHeader.isFinished()) {
return true;
}
Utils.waitFlush(1000);
}
}
private void fillConstantPools(long abortCP) throws IOException {
long thisCP = chunkHeader.getConstantPoolPosition() + chunkHeader.getAbsoluteChunkStart();
long lastCP = -1;
long delta = -1;
boolean logTrace = Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE);
while (thisCP != abortCP && delta != 0) {
input.position(thisCP);
lastCP = thisCP;
int size = input.readInt(); // size
long typeId = input.readLong();
if (typeId != CONSTANT_POOL_TYPE_ID) {
throw new IOException("Expected check point event (id = 1) at position " + lastCP + ", but found type id = " + typeId);
}
input.readLong(); // timestamp
input.readLong(); // duration
delta = input.readLong();
thisCP += delta;
boolean flush = input.readBoolean();
int poolCount = input.readInt();
final long logLastCP = lastCP;
final long logDelta = delta;
if (logTrace) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
return "New constant pool: startPosition=" + logLastCP + ", size=" + size + ", deltaToNext=" + logDelta + ", flush=" + flush + ", poolCount=" + poolCount;
});
}
for (int i = 0; i < poolCount; i++) {
long id = input.readLong(); // type id
ConstantLookup lookup = constantLookups.get(id);
Type type = typeMap.get(id);
if (lookup == null) {
if (type == null) {
throw new IOException(
"Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]");
}
if (type.getName() != CHUNKHEADER) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
}
ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
lookup = new ConstantLookup(pool, type);
constantLookups.put(type.getId(), lookup);
}
Parser parser = parsers.get(id);
if (parser == null) {
throw new IOException("Could not find constant pool type with id = " + id);
}
try {
int count = input.readInt();
if (count == 0) {
throw new InternalError("Pool " + type.getName() + " must contain at least one element ");
}
if (logTrace) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant Pool " + i + ": " + type.getName());
}
for (int j = 0; j < count; j++) {
long key = input.readLong();
Object resolved = lookup.getPreviousResolved(key);
if (resolved == null) {
Object v = parser.parse(input);
logConstant(key, v, false);
lookup.getLatestPool().put(key, v);
} else {
parser.skip(input);
logConstant(key, resolved, true);
lookup.getLatestPool().putResolved(key, resolved);
}
}
} catch (Exception e) {
throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]",
e);
}
}
if (input.position() != lastCP + size) {
throw new IOException("Size of check point event doesn't match content");
}
}
}
private void logConstant(long key, Object v, boolean preresolved) {
if (!Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE)) {
return;
}
String valueText;
if (v.getClass().isArray()) {
Object[] array = (Object[]) v;
StringJoiner sj = new StringJoiner(", ", "{", "}");
for (int i = 0; i < array.length; i++) {
sj.add(textify(array[i]));
}
valueText = sj.toString();
} else {
valueText = textify(v);
}
String suffix = preresolved ? " (presolved)" :"";
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant: " + key + " = " + valueText + suffix);
}
private String textify(Object o) {
if (o == null) { // should not happen
return "null";
}
if (o instanceof String) {
return "\"" + String.valueOf(o) + "\"";
}
if (o instanceof RecordedObject) {
return o.getClass().getName();
}
if (o.getClass().isArray()) {
Object[] array = (Object[]) o;
if (array.length > 0) {
return textify(array[0]) + "[]"; // can it be recursive?
}
}
return String.valueOf(o);
}
private String getName(long id) {
Type type = typeMap.get(id);
return type == null ? ("unknown(" + id + ")") : type.getName();
}
public Collection<Type> getTypes() {
return metadata.getTypes();
}
public List<EventType> getEventTypes() {
return metadata.getEventTypes();
}
public boolean isLastChunk() throws IOException {
return chunkHeader.isLastChunk();
}
ChunkParser newChunkParser() throws IOException {
return new ChunkParser(this);
}
public boolean isChunkFinished() {
return chunkFinished;
}
public void setFlushOperation(Runnable flushOperation) {
this.flushOperation = flushOperation;
}
public long getChunkDuration() {
return chunkHeader.getDurationNanos();
}
public long getStartNanos() {
return chunkHeader.getStartNanos();
}
}
/*
* Copyright (c) 2019, 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.internal.consumer;
import jdk.jfr.internal.Type;
final class ConstantLookup {
private final Type type;
private ConstantMap current;
private ConstantMap previous = ConstantMap.EMPTY;
ConstantLookup(ConstantMap current, Type type) {
this.current = current;
this.type = type;
}
public Type getType() {
return type;
}
public ConstantMap getLatestPool() {
return current;
}
public void newPool() {
previous = current;
current = new ConstantMap(current.factory, current.name);
}
public Object getPreviousResolved(long key) {
return previous.getResolved(key);
}
public Object getCurrentResolved(long key) {
return current.getResolved(key);
}
public Object getCurrent(long key) {
return current.get(key);
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,10 +23,9 @@ ...@@ -23,10 +23,9 @@
* questions. * questions.
*/ */
package jdk.jfr.consumer; package jdk.jfr.internal.consumer;
import java.util.ArrayList; import jdk.jfr.internal.LongMap;
import java.util.List;
/** /**
* Holds mapping between a set of keys and their corresponding object. * Holds mapping between a set of keys and their corresponding object.
...@@ -35,6 +34,15 @@ import java.util.List; ...@@ -35,6 +34,15 @@ import java.util.List;
* {@link ObjectFactory} can be supplied which will instantiate a typed object. * {@link ObjectFactory} can be supplied which will instantiate a typed object.
*/ */
final class ConstantMap { final class ConstantMap {
private static final int RESOLUTION_FINISHED = 0;
private static final int RESOLUTION_STARTED = 1;
public static final ConstantMap EMPTY = new ConstantMap();
// A temporary placeholder, so objects can
// reference themselves (directly, or indirectly),
// when making a transition from numeric id references
// to normal Java references.
private final static class Reference { private final static class Reference {
private final long key; private final long key;
private final ConstantMap pool; private final ConstantMap pool;
...@@ -47,18 +55,28 @@ final class ConstantMap { ...@@ -47,18 +55,28 @@ final class ConstantMap {
Object resolve() { Object resolve() {
return pool.get(key); return pool.get(key);
} }
public String toString() {
return "ref: " + pool.name + "[" + key + "]";
}
} }
private final ObjectFactory<?> factory; final ObjectFactory<?> factory;
final String name;
private final LongMap<Object> objects; private final LongMap<Object> objects;
private LongMap<Boolean> isResolving; private boolean resolving;
private boolean allResolved; private boolean allResolved;
private String name;
private ConstantMap() {
this(null, "<empty>");
allResolved = true;
}
ConstantMap(ObjectFactory<?> factory, String name) { ConstantMap(ObjectFactory<?> factory, String name) {
this.name = name; this.name = name;
this.objects = new LongMap<>(); this.objects = new LongMap<>(2);
this.factory = factory; this.factory = factory;
} }
...@@ -68,26 +86,42 @@ final class ConstantMap { ...@@ -68,26 +86,42 @@ final class ConstantMap {
return objects.get(id); return objects.get(id);
} }
// referenced from a pool, deal with this later // referenced from a pool, deal with this later
if (isResolving == null) { if (!resolving) {
return new Reference(this, id); return new Reference(this, id);
} }
Boolean beingResolved = isResolving.get(id); // should always have a value
Object value = objects.get(id);
if (value == null) {
// unless is 0 which is used to represent null
if (id == 0) {
return null;
}
throw new InternalError("Missing object id=" + id + " in pool " + name + ". All ids should reference object");
}
// we are resolved (but not the whole pool) // id is resolved (but not the whole pool)
if (Boolean.FALSE.equals(beingResolved)) { if (objects.isSetId(id, RESOLUTION_FINISHED)) {
return objects.get(id); return value;
} }
// resolving ourself, abort to avoid infinite recursion // resolving ourself, abort to avoid infinite recursion
if (Boolean.TRUE.equals(beingResolved)) { if (objects.isSetId(id, RESOLUTION_STARTED)) {
return null; return null;
} }
// resolve me! // mark id as STARTED if we should
isResolving.put(id, Boolean.TRUE); // come back during object resolution
Object resolved = resolve(objects.get(id)); objects.setId(id, RESOLUTION_STARTED);
isResolving.put(id, Boolean.FALSE);
// resolve object!
Object resolved = resolve(value);
// mark id as FINISHED
objects.setId(id, RESOLUTION_FINISHED);
// if a factory exists, convert to RecordedThread.
// RecordedClass etc. and store back results
if (factory != null) { if (factory != null) {
Object factorized = factory.createObject(id, resolved); Object factorized = factory.createObject(id, resolved);
objects.put(id, factorized); objects.put(id, factorized);
...@@ -105,7 +139,8 @@ final class ConstantMap { ...@@ -105,7 +139,8 @@ final class ConstantMap {
if (o != null && o.getClass().isArray()) { if (o != null && o.getClass().isArray()) {
final Object[] array = (Object[]) o; final Object[] array = (Object[]) o;
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
array[i] = resolve(array[i]); Object element = array[i];
array[i] = resolve(element);
} }
return array; return array;
} }
...@@ -113,27 +148,37 @@ final class ConstantMap { ...@@ -113,27 +148,37 @@ final class ConstantMap {
} }
public void resolve() { public void resolve() {
List<Long> keyList = new ArrayList<>(); objects.forEachKey(k -> get(k));
objects.keys().forEachRemaining(keyList::add);
for (Long l : keyList) {
get(l);
}
} }
public void put(long key, Object value) { public void put(long key, Object value) {
objects.put(key, value); objects.put(key, value);
} }
public void setIsResolving() { public void setResolving() {
isResolving = new LongMap<>(); resolving = true;
allResolved = false;
} }
public void setResolved() { public void setResolved() {
allResolved = true; allResolved = true;
isResolving = null; // pool finished, release memory resolving = false;
} }
public String getName() { public String getName() {
return name; return name;
} }
public Object getResolved(long id) {
return objects.get(id);
}
public void putResolved(long id, Object object) {
objects.put(id, object);
objects.setId(id, RESOLUTION_FINISHED);
}
public void setAllResolved(boolean allResolved) {
this.allResolved = allResolved;
}
} }
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import jdk.jfr.EventType;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.LongMap;
import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
final class Dispatcher {
final static class EventDispatcher {
private final static EventDispatcher[] NO_DISPATCHERS = new EventDispatcher[0];
private final String eventName;
private final Consumer<RecordedEvent> action;
public EventDispatcher(String eventName, Consumer<RecordedEvent> action) {
this.eventName = eventName;
this.action = action;
}
private void offer(RecordedEvent event) {
action.accept(event);
}
private boolean accepts(EventType eventType) {
return (eventName == null || eventType.getName().equals(eventName));
}
public Consumer<RecordedEvent> getAction() {
return action;
}
}
private final Consumer<Throwable>[] errorActions;
private final Runnable[] flushActions;
private final Runnable[] closeActions;
private final EventDispatcher[] dispatchers;
private final LongMap<EventDispatcher[]> dispatcherLookup = new LongMap<>();
final ParserConfiguration parserConfiguration;
final Instant startTime;
final Instant endTime;
final long startNanos;
final long endNanos;
// Cache
private EventType cacheEventType;
private EventDispatcher[] cacheDispatchers;
@SuppressWarnings({"rawtypes", "unchecked"})
public Dispatcher(StreamConfiguration c) {
this.flushActions = c.flushActions.toArray(new Runnable[0]);
this.closeActions = c.closeActions.toArray(new Runnable[0]);
this.errorActions = c.errorActions.toArray(new Consumer[0]);
this.dispatchers = c.eventActions.toArray(new EventDispatcher[0]);
this.parserConfiguration = new ParserConfiguration(0, Long.MAX_VALUE, c.reuse, c.ordered, buildFilter(dispatchers));
this.startTime = c.startTime;
this.endTime = c.endTime;
this.startNanos = c.startNanos;
this.endNanos = c.endNanos;
}
public void runFlushActions() {
Runnable[] flushActions = this.flushActions;
for (int i = 0; i < flushActions.length; i++) {
try {
flushActions[i].run();
} catch (Exception e) {
handleError(e);
}
}
}
public void runCloseActions() {
Runnable[] closeActions = this.closeActions;
for (int i = 0; i < closeActions.length; i++) {
try {
closeActions[i].run();
} catch (Exception e) {
handleError(e);
}
}
}
private static ParserFilter buildFilter(EventDispatcher[] dispatchers) {
ParserFilter ef = new ParserFilter();
for (EventDispatcher ed : dispatchers) {
String name = ed.eventName;
if (name == null) {
return ParserFilter.ACCEPT_ALL;
}
ef.setThreshold(name, 0);
}
return ef;
}
void dispatch(RecordedEvent event) {
EventType type = event.getEventType();
EventDispatcher[] dispatchers = null;
if (type == cacheEventType) {
dispatchers = cacheDispatchers;
} else {
dispatchers = dispatcherLookup.get(type.getId());
if (dispatchers == null) {
List<EventDispatcher> list = new ArrayList<>();
for (EventDispatcher e : this.dispatchers) {
if (e.accepts(type)) {
list.add(e);
}
}
dispatchers = list.isEmpty() ? EventDispatcher.NO_DISPATCHERS : list.toArray(new EventDispatcher[0]);
dispatcherLookup.put(type.getId(), dispatchers);
}
cacheDispatchers = dispatchers;
}
// Expected behavior if exception occurs in onEvent:
//
// Synchronous:
// - User has added onError action:
// Catch exception, call onError and continue with next event
// Let Errors propagate to caller of EventStream::start
// - Default action
// Catch exception, e.printStackTrace() and continue with next event
// Let Errors propagate to caller of EventStream::start
//
// Asynchronous
// - User has added onError action
// Catch exception, call onError and continue with next event
// Let Errors propagate, shutdown thread and stream
// - Default action
// Catch exception, e.printStackTrace() and continue with next event
// Let Errors propagate and shutdown thread and stream
//
for (int i = 0; i < dispatchers.length; i++) {
try {
dispatchers[i].offer(event);
} catch (Exception e) {
handleError(e);
}
}
}
private void handleError(Throwable e) {
Consumer<?>[] consumers = this.errorActions;
if (consumers.length == 0) {
defaultErrorHandler(e);
return;
}
for (int i = 0; i < consumers.length; i++) {
@SuppressWarnings("unchecked")
Consumer<Throwable> consumer = (Consumer<Throwable>) consumers[i];
consumer.accept(e);
}
}
private void defaultErrorHandler(Throwable e) {
e.printStackTrace();
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.time.Instant;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.JVM;
import jdk.jfr.internal.Utils;
import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
/**
* Implementation of an {@code EventStream}} that operates against a directory
* with chunk files.
*
*/
public final class EventDirectoryStream extends AbstractEventStream {
private final static Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
private final RepositoryFiles repositoryFiles;
private final boolean active;
private final FileAccess fileAccess;
private ChunkParser currentParser;
private long currentChunkStartNanos;
private RecordedEvent[] sortedCache;
private int threadExclusionLevel = 0;
public EventDirectoryStream(AccessControlContext acc, Path p, FileAccess fileAccess, boolean active) throws IOException {
super(acc, active);
this.fileAccess = Objects.requireNonNull(fileAccess);
this.active = active;
this.repositoryFiles = new RepositoryFiles(fileAccess, p);
}
@Override
public void close() {
setClosed(true);
dispatcher().runCloseActions();
repositoryFiles.close();
}
@Override
public void start() {
start(Utils.timeToNanos(Instant.now()));
}
@Override
public void startAsync() {
startAsync(Utils.timeToNanos(Instant.now()));
}
@Override
protected void process() throws IOException {
JVM jvm = JVM.getJVM();
Thread t = Thread.currentThread();
try {
if (jvm.isExcluded(t)) {
threadExclusionLevel++;
} else {
jvm.exclude(t);
}
processRecursionSafe();
} finally {
if (threadExclusionLevel > 0) {
threadExclusionLevel--;
} else {
jvm.include(t);
}
}
}
protected void processRecursionSafe() throws IOException {
Dispatcher disp = dispatcher();
Path path;
boolean validStartTime = active || disp.startTime != null;
if (validStartTime) {
path = repositoryFiles.firstPath(disp.startNanos);
} else {
path = repositoryFiles.lastPath();
}
if (path == null) { // closed
return;
}
currentChunkStartNanos = repositoryFiles.getTimestamp(path);
try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
currentParser = new ChunkParser(input, disp.parserConfiguration);
long segmentStart = currentParser.getStartNanos() + currentParser.getChunkDuration();
long filterStart = validStartTime ? disp.startNanos : segmentStart;
long filterEnd = disp.endTime != null ? disp.endNanos: Long.MAX_VALUE;
while (!isClosed()) {
boolean awaitnewEvent = false;
while (!isClosed() && !currentParser.isChunkFinished()) {
disp = dispatcher();
ParserConfiguration pc = disp.parserConfiguration;
pc.filterStart = filterStart;
pc.filterEnd = filterEnd;
currentParser.updateConfiguration(pc, true);
currentParser.setFlushOperation(getFlushOperation());
if (pc.isOrdered()) {
awaitnewEvent = processOrdered(disp, awaitnewEvent);
} else {
awaitnewEvent = processUnordered(disp, awaitnewEvent);
}
if (currentParser.getStartNanos() + currentParser.getChunkDuration() > filterEnd) {
close();
return;
}
}
if (isClosed()) {
return;
}
long durationNanos = currentParser.getChunkDuration();
if (durationNanos == 0) {
// Avoid reading the same chunk again and again if
// duration is 0 ns
durationNanos++;
}
path = repositoryFiles.nextPath(currentChunkStartNanos + durationNanos);
if (path == null) {
return; // stream closed
}
currentChunkStartNanos = repositoryFiles.getTimestamp(path);
input.setFile(path);
currentParser = currentParser.newChunkParser();
// TODO: Optimization. No need filter when we reach new chunk
// Could set start = 0;
}
}
}
private boolean processOrdered(Dispatcher c, boolean awaitNewEvents) throws IOException {
if (sortedCache == null) {
sortedCache = new RecordedEvent[100_000];
}
int index = 0;
while (true) {
RecordedEvent e = currentParser.readStreamingEvent(awaitNewEvents);
if (e == null) {
// wait for new event with next call to
// readStreamingEvent()
awaitNewEvents = true;
break;
}
awaitNewEvents = false;
if (index == sortedCache.length) {
sortedCache = Arrays.copyOf(sortedCache, sortedCache.length * 2);
}
sortedCache[index++] = e;
}
// no events found
if (index == 0 && currentParser.isChunkFinished()) {
return awaitNewEvents;
}
// at least 2 events, sort them
if (index > 1) {
Arrays.sort(sortedCache, 0, index, EVENT_COMPARATOR);
}
for (int i = 0; i < index; i++) {
c.dispatch(sortedCache[i]);
}
return awaitNewEvents;
}
private boolean processUnordered(Dispatcher c, boolean awaitNewEvents) throws IOException {
while (true) {
RecordedEvent e = currentParser.readStreamingEvent(awaitNewEvents);
if (e == null) {
return true;
} else {
c.dispatch(e);
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.consumer.Dispatcher;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput;
/**
* Implementation of an event stream that operates against a recording file.
*
*/
public final class EventFileStream extends AbstractEventStream {
private final static Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
private final RecordingInput input;
private ChunkParser currentParser;
private RecordedEvent[] cacheSorted;
public EventFileStream(AccessControlContext acc, Path path) throws IOException {
super(acc, false);
Objects.requireNonNull(path);
this.input = new RecordingInput(path.toFile(), FileAccess.UNPRIVILIGED);
}
@Override
public void start() {
start(0);
}
@Override
public void startAsync() {
startAsync(0);
}
@Override
public void close() {
setClosed(true);
dispatcher().runCloseActions();
try {
input.close();
} catch (IOException e) {
// ignore
}
}
@Override
protected void process() throws IOException {
Dispatcher disp = dispatcher();
long start = 0;
long end = Long.MAX_VALUE;
if (disp.startTime != null) {
start = disp.startNanos;
}
if (disp.endTime != null) {
end = disp.endNanos;
}
currentParser = new ChunkParser(input, disp.parserConfiguration);
while (!isClosed()) {
if (currentParser.getStartNanos() > end) {
close();
return;
}
disp = dispatcher();
disp.parserConfiguration.filterStart = start;
disp.parserConfiguration.filterEnd = end;
currentParser.updateConfiguration(disp.parserConfiguration, true);
currentParser.setFlushOperation(getFlushOperation());
if (disp.parserConfiguration.isOrdered()) {
processOrdered(disp);
} else {
processUnordered(disp);
}
if (isClosed() || currentParser.isLastChunk()) {
return;
}
currentParser = currentParser.nextChunkParser();
}
}
private void processOrdered(Dispatcher c) throws IOException {
if (cacheSorted == null) {
cacheSorted = new RecordedEvent[10_000];
}
RecordedEvent event;
int index = 0;
while (true) {
event = currentParser.readEvent();
if (event == null) {
Arrays.sort(cacheSorted, 0, index, EVENT_COMPARATOR);
for (int i = 0; i < index; i++) {
c.dispatch(cacheSorted[i]);
}
return;
}
if (index == cacheSorted.length) {
RecordedEvent[] tmp = cacheSorted;
cacheSorted = new RecordedEvent[2 * tmp.length];
System.arraycopy(tmp, 0, cacheSorted, 0, tmp.length);
}
cacheSorted[index++] = event;
}
}
private void processUnordered(Dispatcher c) throws IOException {
while (!isClosed()) {
RecordedEvent event = currentParser.readEvent();
if (event == null) {
return;
}
c.dispatch(event);
}
}
}
/*
* Copyright (c) 2016, 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
import java.io.IOException;
import java.util.List;
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.consumer.Parser;
import jdk.jfr.internal.consumer.RecordingInput;
/**
* Parses an event and returns a {@link RecordedEvent}.
*
*/
final class EventParser extends Parser {
private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
private final Parser[] parsers;
private final EventType eventType;
private final TimeConverter timeConverter;
private final boolean hasDuration;
private final List<ValueDescriptor> valueDescriptors;
private final int startIndex;
private final int length;
private final RecordedEvent unorderedEvent;
private final ObjectContext objectContext;
private RecordedEvent[] cached;
private int cacheIndex;
private boolean enabled = true;
private boolean ordered;
private long filterStart;
private long filterEnd = Long.MAX_VALUE;
private long thresholdNanos = -1;
EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
this.timeConverter = timeConverter;
this.parsers = parsers;
this.eventType = type;
this.hasDuration = type.getField(FIELD_DURATION) != null;
this.startIndex = hasDuration ? 2 : 1;
this.length = parsers.length - startIndex;
this.valueDescriptors = type.getFields();
this.objectContext = new ObjectContext(type, valueDescriptors, timeConverter);
this.unorderedEvent = PRIVATE_ACCESS.newRecordedEvent(objectContext, new Object[length], 0L, 0L);
}
private RecordedEvent cachedEvent() {
if (ordered) {
if (cacheIndex == cached.length) {
RecordedEvent[] old = cached;
cached = new RecordedEvent[cached.length * 2];
System.arraycopy(old, 0, cached, 0, old.length);
}
RecordedEvent event = cached[cacheIndex];
if (event == null) {
event = PRIVATE_ACCESS.newRecordedEvent(objectContext, new Object[length], 0L, 0L);
cached[cacheIndex] = event;
}
cacheIndex++;
return event;
} else {
return unorderedEvent;
}
}
public EventType getEventType() {
return eventType;
}
public void setThresholdNanos(long thresholdNanos) {
this.thresholdNanos = thresholdNanos;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
public RecordedEvent parse(RecordingInput input) throws IOException {
if (!enabled) {
return null;
}
long startTicks = input.readLong();
long endTicks = startTicks;
if (hasDuration) {
long durationTicks = input.readLong();
if (thresholdNanos > 0L) {
if (timeConverter.convertTimespan(durationTicks) < thresholdNanos) {
return null;
}
}
endTicks += durationTicks;
}
if (filterStart != 0L || filterEnd != Long.MAX_VALUE) {
long eventEnd = timeConverter.convertTimestamp(endTicks);
if (eventEnd < filterStart) {
return null;
}
if (eventEnd > filterEnd) {
return null;
}
}
if (cached != null) {
RecordedEvent event = cachedEvent();
JdkJfrConsumer access = PRIVATE_ACCESS;
access.setStartTicks(event, startTicks);
access.setEndTicks(event, endTicks);
Object[] values = access.eventValues(event);
for (int i = 0; i < values.length; i++) {
values[i] = parsers[startIndex + i].parse(input);
}
return event;
}
Object[] values = new Object[length];
for (int i = 0; i < values.length; i++) {
values[i] = parsers[startIndex + i].parse(input);
}
return PRIVATE_ACCESS.newRecordedEvent(objectContext, values, startTicks, endTicks);
}
@Override
public void skip(RecordingInput input) throws IOException {
throw new InternalError("Should not call this method. More efficent to read event size and skip ahead");
}
public void resetCache() {
cacheIndex = 0;
}
private boolean hasReuse() {
return cached != null;
}
public void setReuse(boolean reuse) {
if (reuse == hasReuse()) {
return;
}
if (reuse) {
cached = new RecordedEvent[2];
cacheIndex = 0;
} else {
cached = null;
}
}
public void setFilterStart(long filterStart) {
this.filterStart = filterStart;
}
public void setFilterEnd(long filterEnd) {
this.filterEnd = filterEnd;
}
public void setOrdered(boolean ordered) {
if (this.ordered == ordered) {
return;
}
this.ordered = ordered;
this.cacheIndex = 0;
}
}
/*
* Copyright (c) 2019, 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.internal.consumer;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
// Protected by modular boundaries.
public abstract class FileAccess {
public final static FileAccess UNPRIVILIGED = new UnPriviliged();
public abstract RandomAccessFile openRAF(File f, String mode) throws IOException;
public abstract DirectoryStream<Path> newDirectoryStream(Path repository) throws IOException;
public abstract String getAbsolutePath(File f) throws IOException;
public abstract long length(File f) throws IOException;
public abstract long fileSize(Path p) throws IOException;
private static class UnPriviliged extends FileAccess {
@Override
public RandomAccessFile openRAF(File f, String mode) throws IOException {
return new RandomAccessFile(f, mode);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir) throws IOException {
return Files.newDirectoryStream(dir);
}
@Override
public String getAbsolutePath(File f) throws IOException {
return f.getAbsolutePath();
}
@Override
public long length(File f) throws IOException {
return f.length();
}
@Override
public long fileSize(Path p) throws IOException {
return Files.size(p);
}
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,63 +23,79 @@ ...@@ -23,63 +23,79 @@
* questions. * questions.
*/ */
package jdk.jfr.consumer; package jdk.jfr.internal.consumer;
import java.io.IOException;
import java.util.Comparator;
import java.util.List; import java.util.List;
import jdk.jfr.ValueDescriptor; import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedClassLoader;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedFrame;
import jdk.jfr.consumer.RecordedMethod;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.consumer.RecordedThread;
import jdk.jfr.consumer.RecordedThreadGroup;
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.Type; import jdk.jfr.internal.Type;
/*
/** * Purpose of this class is to give package private access to
* Abstract factory for creating specialized types * the jdk.jfr.consumer package
*/ */
abstract class ObjectFactory<T> { public abstract class JdkJfrConsumer {
final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types."; private static JdkJfrConsumer instance;
final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame"; // Initialization will trigger setAccess being called
final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame"; private static void forceInitializetion() {
try {
public static ObjectFactory<?> create(Type type, TimeConverter timeConverter) { Class<?> c = RecordedObject.class;
switch (type.getName()) { Class.forName(c.getName(), true, c.getClassLoader());
case "java.lang.Thread": } catch (ClassNotFoundException e) {
return RecordedThread.createFactory(type, timeConverter); throw new InternalError("Should not happen");
case TYPE_PREFIX_VERSION_1 + "StackFrame":
case TYPE_PREFIX_VERSION_2 + "StackFrame":
return RecordedFrame.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "Method":
case TYPE_PREFIX_VERSION_2 + "Method":
return RecordedMethod.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
return RecordedThreadGroup.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "StackTrace":
case TYPE_PREFIX_VERSION_2 + "StackTrace":
return RecordedStackTrace.createFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "ClassLoader":
case TYPE_PREFIX_VERSION_2 + "ClassLoader":
return RecordedClassLoader.createFactory(type, timeConverter);
case "java.lang.Class":
return RecordedClass.createFactory(type, timeConverter);
} }
return null;
} }
private final List<ValueDescriptor> valueDescriptors; public static void setAccess(JdkJfrConsumer access) {
instance = access;
ObjectFactory(Type type) {
this.valueDescriptors = type.getFields();
} }
T createObject(long id, Object value) { public static JdkJfrConsumer instance() {
if (value == null) { if (instance == null) {
return null; forceInitializetion();
} }
if (value instanceof Object[]) { return instance;
return createTyped(valueDescriptors, id, (Object[]) value);
}
throw new InternalError("Object factory must have struct type");
} }
abstract T createTyped(List<ValueDescriptor> valueDescriptors, long id, Object[] values); public abstract List<Type> readTypes(RecordingFile file) throws IOException;
public abstract boolean isLastEventInChunk(RecordingFile file);
public abstract Object getOffsetDataTime(RecordedObject event, String name);
public abstract RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values);
public abstract RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values);
public abstract RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values);
public abstract RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values);
public abstract RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values);
public abstract RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values);
public abstract RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values);
public abstract RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] objects, long l, long m);
public abstract Comparator<? super RecordedEvent> eventComparator();
public abstract void setStartTicks(RecordedEvent event, long startTicks);
public abstract void setEndTicks(RecordedEvent event, long endTicks);
public abstract Object[] eventValues(RecordedEvent event);
} }
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,50 +23,55 @@ ...@@ -23,50 +23,55 @@
* questions. * questions.
*/ */
package jdk.jfr.consumer; package jdk.jfr.internal.consumer;
import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION; import java.time.ZoneId;
import java.util.HashMap;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map;
import jdk.jfr.EventType; import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.consumer.RecordingInput;
/** public final class ObjectContext {
* Parses an event and returns a {@link RecordedEvent}. private final Map<ValueDescriptor, ObjectContext> contextLookup;
*
*/
final class EventParser extends Parser {
private final Parser[] parsers;
private final EventType eventType;
private final TimeConverter timeConverter; private final TimeConverter timeConverter;
private final boolean hasDuration;
private final List<ValueDescriptor> valueDescriptors;
EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) { public final EventType eventType;
public final List<ValueDescriptor> fields;
ObjectContext(EventType eventType, List<ValueDescriptor> fields, TimeConverter timeConverter) {
this.contextLookup = new HashMap<>();
this.eventType = eventType;
this.fields = fields;
this.timeConverter = timeConverter; this.timeConverter = timeConverter;
this.parsers = parsers;
this.eventType = type;
this.hasDuration = type.getField(FIELD_DURATION) != null;
this.valueDescriptors = type.getFields();
} }
@Override private ObjectContext(ObjectContext parent, ValueDescriptor descriptor) {
public Object parse(RecordingInput input) throws IOException { this.eventType = parent.eventType;
Object[] values = new Object[parsers.length]; this.contextLookup = parent.contextLookup;
for (int i = 0; i < parsers.length; i++) { this.timeConverter = parent.timeConverter;
values[i] = parsers[i].parse(input); this.fields = descriptor.getFields();
} }
Long startTicks = (Long) values[0];
long startTime = timeConverter.convertTimestamp(startTicks); public ObjectContext getInstance(ValueDescriptor descriptor) {
if (hasDuration) { ObjectContext context = contextLookup.get(descriptor);
long durationTicks = (Long) values[1]; if (context == null) {
long endTime = timeConverter.convertTimestamp(startTicks + durationTicks); context = new ObjectContext(this, descriptor);
return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter); contextLookup.put(descriptor, context);
} else {
return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
} }
return context;
}
public long convertTimestamp(long ticks) {
return timeConverter.convertTimestamp(ticks);
}
public long convertTimespan(long ticks) {
return timeConverter.convertTimespan(ticks);
}
public ZoneId getZoneOffset() {
return timeConverter.getZoneOffset();
} }
} }
/*
* Copyright (c) 2016, 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedClassLoader;
import jdk.jfr.consumer.RecordedFrame;
import jdk.jfr.consumer.RecordedMethod;
import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.consumer.RecordedThread;
import jdk.jfr.consumer.RecordedThreadGroup;
import jdk.jfr.internal.Type;
/**
* Abstract factory for creating specialized types
*/
public abstract class ObjectFactory<T> {
private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
private final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
private final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
public final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
public final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
switch (type.getName()) {
case "java.lang.Thread":
return createThreadFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "StackFrame":
case TYPE_PREFIX_VERSION_2 + "StackFrame":
return createFrameFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "Method":
case TYPE_PREFIX_VERSION_2 + "Method":
return createMethodFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
return createdThreadGroupFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "StackTrace":
case TYPE_PREFIX_VERSION_2 + "StackTrace":
return createStackTraceFactory(type, timeConverter);
case TYPE_PREFIX_VERSION_1 + "ClassLoader":
case TYPE_PREFIX_VERSION_2 + "ClassLoader":
return createClassLoaderFactory(type, timeConverter);
case "java.lang.Class":
return createClassFactory(type, timeConverter);
}
return null;
}
private static ObjectFactory<RecordedClass> createClassFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedClass>(type, timeConverter) {
@Override
RecordedClass createTyped(ObjectContext objectContext, long id, Object[] values) {
return PRIVATE_ACCESS.newRecordedClass(objectContext, id, values);
}
};
}
private static ObjectFactory<?> createClassLoaderFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedClassLoader>(type, timeConverter) {
@Override
RecordedClassLoader createTyped(ObjectContext objectContext, long id, Object[] values) {
return PRIVATE_ACCESS.newRecordedClassLoader(objectContext, id, values);
}
};
}
private static ObjectFactory<RecordedStackTrace> createStackTraceFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedStackTrace>(type, timeConverter) {
@Override
RecordedStackTrace createTyped(ObjectContext objectContext, long id, Object[] values) {
return PRIVATE_ACCESS.newRecordedStackTrace(objectContext, values);
}
};
}
private static ObjectFactory<RecordedThreadGroup> createdThreadGroupFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedThreadGroup>(type, timeConverter) {
@Override
RecordedThreadGroup createTyped(ObjectContext objectContext, long id, Object[] values) {
return PRIVATE_ACCESS.newRecordedThreadGroup(objectContext, values);
}
};
}
private static ObjectFactory<RecordedMethod> createMethodFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedMethod>(type, timeConverter) {
@Override
RecordedMethod createTyped(ObjectContext objectContext, long id, Object[] values) {
return PRIVATE_ACCESS.newRecordedMethod(objectContext, values);
}
};
}
private static ObjectFactory<RecordedFrame> createFrameFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedFrame>(type, timeConverter) {
@Override
RecordedFrame createTyped(ObjectContext objectContext, long id, Object[] values) {
return PRIVATE_ACCESS.newRecordedFrame(objectContext, values);
}
};
}
private static ObjectFactory<RecordedThread> createThreadFactory(Type type, TimeConverter timeConverter) {
return new ObjectFactory<RecordedThread>(type, timeConverter) {
@Override
RecordedThread createTyped(ObjectContext objectContext, long id, Object[] values) {
return PRIVATE_ACCESS.newRecordedThread(objectContext, id, values);
}
};
}
private final ObjectContext objectContext;
private ObjectFactory(Type type, TimeConverter timeConverter) {
this.objectContext = new ObjectContext(null, type.getFields(), timeConverter);
}
T createObject(long id, Object value) {
if (value == null) {
return null;
}
if (value instanceof Object[]) {
return createTyped(objectContext, id, (Object[]) value);
}
throw new InternalError("Object factory must have struct type. Type was " + value.getClass().getName());
}
abstract T createTyped(ObjectContext objectContextm, long id, Object[] values);
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,12 +23,10 @@ ...@@ -23,12 +23,10 @@
* questions. * questions.
*/ */
package jdk.jfr.consumer; package jdk.jfr.internal.consumer;
import java.io.IOException; import java.io.IOException;
import jdk.jfr.internal.consumer.RecordingInput;
/** /**
* Base class for parsing data from a {@link RecordingInput}. * Base class for parsing data from a {@link RecordingInput}.
*/ */
...@@ -41,5 +39,14 @@ abstract class Parser { ...@@ -41,5 +39,14 @@ abstract class Parser {
* @throws IOException if operation couldn't be completed due to I/O * @throws IOException if operation couldn't be completed due to I/O
* problems * problems
*/ */
abstract Object parse(RecordingInput input) throws IOException; public abstract Object parse(RecordingInput input) throws IOException;
/**
* Skips data that would usually be by parsed the {@code #parse(RecordingInput)} method.
*
* @param input input to read from
* @throws IOException if operation couldn't be completed due to I/O
* problems
*/
public abstract void skip(RecordingInput input) throws IOException;
} }
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,18 +23,19 @@ ...@@ -23,18 +23,19 @@
* questions. * questions.
*/ */
package jdk.jfr.consumer; package jdk.jfr.internal.consumer;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import jdk.jfr.EventType; import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.LongMap;
import jdk.jfr.internal.MetadataDescriptor; import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.PrivateAccess; import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Type; import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.Parser;
import jdk.jfr.internal.consumer.RecordingInput; import jdk.jfr.internal.consumer.RecordingInput;
/** /**
...@@ -44,21 +45,25 @@ final class ParserFactory { ...@@ -44,21 +45,25 @@ final class ParserFactory {
private final LongMap<Parser> parsers = new LongMap<>(); private final LongMap<Parser> parsers = new LongMap<>();
private final TimeConverter timeConverter; private final TimeConverter timeConverter;
private final LongMap<Type> types = new LongMap<>(); private final LongMap<Type> types = new LongMap<>();
private final LongMap<ConstantMap> constantPools; private final LongMap<ConstantLookup> constantLookups;
public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException { public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException {
this.constantPools = new LongMap<>(); this.constantLookups = constantLookups;
this.timeConverter = timeConverter; this.timeConverter = timeConverter;
for (Type t : metadata.getTypes()) { for (Type t : metadata.getTypes()) {
types.put(t.getId(), t); types.put(t.getId(), t);
} }
for (Type t : types) { // Add to separate list
// so createCompositeParser can throw
// IOException outside lambda
List<Type> typeList = new ArrayList<>();
types.forEach(typeList::add);
for (Type t : typeList) {
if (!t.getFields().isEmpty()) { // Avoid primitives if (!t.getFields().isEmpty()) { // Avoid primitives
CompositeParser cp = createCompositeParser(t); CompositeParser cp = createCompositeParser(t, false);
if (t.isSimpleType()) { // Reduce to nested parser if (t.isSimpleType()) { // Reduce to nested parser
parsers.put(t.getId(), cp.parsers[0]); parsers.put(t.getId(), cp.parsers[0]);
} }
} }
} }
// Override event types with event parsers // Override event types with event parsers
...@@ -71,10 +76,6 @@ final class ParserFactory { ...@@ -71,10 +76,6 @@ final class ParserFactory {
return parsers; return parsers;
} }
public LongMap<ConstantMap> getConstantPools() {
return constantPools;
}
public LongMap<Type> getTypeMap() { public LongMap<Type> getTypeMap() {
return types; return types;
} }
...@@ -82,17 +83,17 @@ final class ParserFactory { ...@@ -82,17 +83,17 @@ final class ParserFactory {
private EventParser createEventParser(EventType eventType) throws IOException { private EventParser createEventParser(EventType eventType) throws IOException {
List<Parser> parsers = new ArrayList<Parser>(); List<Parser> parsers = new ArrayList<Parser>();
for (ValueDescriptor f : eventType.getFields()) { for (ValueDescriptor f : eventType.getFields()) {
parsers.add(createParser(f)); parsers.add(createParser(f, true));
} }
return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0])); return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
} }
private Parser createParser(ValueDescriptor v) throws IOException { private Parser createParser(ValueDescriptor v, boolean event) throws IOException {
boolean constantPool = PrivateAccess.getInstance().isConstantPool(v); boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
if (v.isArray()) { if (v.isArray()) {
Type valueType = PrivateAccess.getInstance().getType(v); Type valueType = PrivateAccess.getInstance().getType(v);
ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null); ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
return new ArrayParser(createParser(element)); return new ArrayParser(createParser(element, event));
} }
long id = v.getTypeId(); long id = v.getTypeId();
Type type = types.get(id); Type type = types.get(id);
...@@ -100,25 +101,29 @@ final class ParserFactory { ...@@ -100,25 +101,29 @@ final class ParserFactory {
throw new IOException("Type '" + v.getTypeName() + "' is not defined"); throw new IOException("Type '" + v.getTypeName() + "' is not defined");
} }
if (constantPool) { if (constantPool) {
ConstantMap pool = constantPools.get(id); ConstantLookup lookup = constantLookups.get(id);
if (pool == null) { if (lookup == null) {
pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
constantPools.put(id, pool); lookup = new ConstantLookup(pool, type);
constantLookups.put(id, lookup);
}
if (event) {
return new EventValueConstantParser(lookup);
} }
return new ConstantMapValueParser(pool); return new ConstantValueParser(lookup);
} }
Parser parser = parsers.get(id); Parser parser = parsers.get(id);
if (parser == null) { if (parser == null) {
if (!v.getFields().isEmpty()) { if (!v.getFields().isEmpty()) {
return createCompositeParser(type); return createCompositeParser(type, event);
} else { } else {
return registerParserType(type, createPrimitiveParser(type)); return registerParserType(type, createPrimitiveParser(type, constantPool));
} }
} }
return parser; return parser;
} }
private Parser createPrimitiveParser(Type type) throws IOException { private Parser createPrimitiveParser(Type type, boolean event) throws IOException {
switch (type.getName()) { switch (type.getName()) {
case "int": case "int":
return new IntegerParser(); return new IntegerParser();
...@@ -138,8 +143,9 @@ final class ParserFactory { ...@@ -138,8 +143,9 @@ final class ParserFactory {
return new ByteParser(); return new ByteParser();
case "java.lang.String": case "java.lang.String":
ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
constantPools.put(type.getId(), pool); ConstantLookup lookup = new ConstantLookup(pool, type);
return new StringParser(pool); constantLookups.put(type.getId(), lookup);
return new StringParser(lookup, event);
default: default:
throw new IOException("Unknown primitive type " + type.getName()); throw new IOException("Unknown primitive type " + type.getName());
} }
...@@ -155,7 +161,7 @@ final class ParserFactory { ...@@ -155,7 +161,7 @@ final class ParserFactory {
return parser; return parser;
} }
private CompositeParser createCompositeParser(Type type) throws IOException { private CompositeParser createCompositeParser(Type type, boolean event) throws IOException {
List<ValueDescriptor> vds = type.getFields(); List<ValueDescriptor> vds = type.getFields();
Parser[] parsers = new Parser[vds.size()]; Parser[] parsers = new Parser[vds.size()];
CompositeParser composite = new CompositeParser(parsers); CompositeParser composite = new CompositeParser(parsers);
...@@ -164,7 +170,7 @@ final class ParserFactory { ...@@ -164,7 +170,7 @@ final class ParserFactory {
int index = 0; int index = 0;
for (ValueDescriptor vd : vds) { for (ValueDescriptor vd : vds) {
parsers[index++] = createParser(vd); parsers[index++] = createParser(vd, event);
} }
return composite; return composite;
} }
...@@ -174,6 +180,11 @@ final class ParserFactory { ...@@ -174,6 +180,11 @@ final class ParserFactory {
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
} }
@Override
public void skip(RecordingInput input) throws IOException {
input.skipBytes(1);
}
} }
private static final class ByteParser extends Parser { private static final class ByteParser extends Parser {
...@@ -181,19 +192,51 @@ final class ParserFactory { ...@@ -181,19 +192,51 @@ final class ParserFactory {
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return Byte.valueOf(input.readByte()); return Byte.valueOf(input.readByte());
} }
@Override
public void skip(RecordingInput input) throws IOException {
input.skipBytes(1);
}
} }
private static final class LongParser extends Parser { private static final class LongParser extends Parser {
private Object lastLongObject = Long.valueOf(0);
private long last = 0;
@Override @Override
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return Long.valueOf(input.readLong()); long l = input.readLong();
if (l == last) {
return lastLongObject;
}
last = l;
lastLongObject = Long.valueOf(l);
return lastLongObject;
}
@Override
public void skip(RecordingInput input) throws IOException {
input.readLong();
} }
} }
private static final class IntegerParser extends Parser { private static final class IntegerParser extends Parser {
private Integer lastIntegergObject = Integer.valueOf(0);
private int last = 0;
@Override @Override
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return Integer.valueOf(input.readInt()); int i = input.readInt();
if (i != last) {
last = i;
lastIntegergObject = Integer.valueOf(i);
}
return lastIntegergObject;
}
@Override
public void skip(RecordingInput input) throws IOException {
input.readInt();
} }
} }
...@@ -202,6 +245,11 @@ final class ParserFactory { ...@@ -202,6 +245,11 @@ final class ParserFactory {
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return Short.valueOf(input.readShort()); return Short.valueOf(input.readShort());
} }
@Override
public void skip(RecordingInput input) throws IOException {
input.readShort();
}
} }
private static final class CharacterParser extends Parser { private static final class CharacterParser extends Parser {
...@@ -209,6 +257,11 @@ final class ParserFactory { ...@@ -209,6 +257,11 @@ final class ParserFactory {
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return Character.valueOf(input.readChar()); return Character.valueOf(input.readChar());
} }
@Override
public void skip(RecordingInput input) throws IOException {
input.readChar();
}
} }
private static final class FloatParser extends Parser { private static final class FloatParser extends Parser {
...@@ -216,40 +269,22 @@ final class ParserFactory { ...@@ -216,40 +269,22 @@ final class ParserFactory {
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return Float.valueOf(input.readFloat()); return Float.valueOf(input.readFloat());
} }
}
private static final class DoubleParser extends Parser {
@Override @Override
public Object parse(RecordingInput input) throws IOException { public void skip(RecordingInput input) throws IOException {
return Double.valueOf(input.readDouble()); input.skipBytes(Float.SIZE);
} }
} }
private static final class StringParser extends Parser { private static final class DoubleParser extends Parser {
private final ConstantMap stringConstantMap;
private String last;
StringParser(ConstantMap stringConstantMap) {
this.stringConstantMap = stringConstantMap;
}
@Override @Override
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
String s = parseEncodedString(input); return Double.valueOf(input.readDouble());
if (!Objects.equals(s, last)) {
last = s;
}
return last;
} }
private String parseEncodedString(RecordingInput input) throws IOException { @Override
byte encoding = input.readByte(); public void skip(RecordingInput input) throws IOException {
if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) { input.skipBytes(Double.SIZE);
long id = input.readLong();
return (String) stringConstantMap.get(id);
} else {
return input.readEncodedString(encoding);
}
} }
} }
...@@ -269,6 +304,14 @@ final class ParserFactory { ...@@ -269,6 +304,14 @@ final class ParserFactory {
} }
return array; return array;
} }
@Override
public void skip(RecordingInput input) throws IOException {
final int size = input.readInt();
for (int i = 0; i < size; i++) {
elementParser.skip(input);
}
}
} }
private final static class CompositeParser extends Parser { private final static class CompositeParser extends Parser {
...@@ -286,18 +329,54 @@ final class ParserFactory { ...@@ -286,18 +329,54 @@ final class ParserFactory {
} }
return values; return values;
} }
@Override
public void skip(RecordingInput input) throws IOException {
for (int i = 0; i < parsers.length; i++) {
parsers[i].skip(input);
}
}
} }
private static final class ConstantMapValueParser extends Parser { private static final class EventValueConstantParser extends Parser {
private final ConstantMap pool; private final ConstantLookup lookup;
private Object lastValue = 0;
private long lastKey = -1;
EventValueConstantParser(ConstantLookup lookup) {
this.lookup = lookup;
}
ConstantMapValueParser(ConstantMap pool) { @Override
this.pool = pool; public Object parse(RecordingInput input) throws IOException {
long key = input.readLong();
if (key == lastKey) {
return lastValue;
}
lastKey = key;
lastValue = lookup.getCurrentResolved(key);
return lastValue;
}
@Override
public void skip(RecordingInput input) throws IOException {
input.readLong();
}
}
private static final class ConstantValueParser extends Parser {
private final ConstantLookup lookup;
ConstantValueParser(ConstantLookup lookup) {
this.lookup = lookup;
} }
@Override @Override
public Object parse(RecordingInput input) throws IOException { public Object parse(RecordingInput input) throws IOException {
return pool.get(input.readLong()); return lookup.getCurrent(input.readLong());
}
@Override
public void skip(RecordingInput input) throws IOException {
input.readLong();
} }
} }
} }
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
final class ParserFilter {
public static final ParserFilter ACCEPT_ALL = new ParserFilter(true, Collections.emptyMap());
private final Map<String, Long> thresholds;
private final boolean acceptAll;
public ParserFilter() {
this(false, new HashMap<>());
}
private ParserFilter(boolean acceptAll, Map<String, Long> thresholds) {
this.acceptAll = acceptAll;
this.thresholds = thresholds;
}
public void setThreshold(String eventName, long nanos) {
Long l = thresholds.get(eventName);
if (l != null) {
l = Math.min(l, nanos);
} else {
l = nanos;
}
thresholds.put(eventName, l);
}
public long getThreshold(String eventName) {
if (acceptAll) {
return 0L;
}
Long l = thresholds.get(eventName);
if (l != null) {
return l;
}
return -1;
}
public String toString() {
if (acceptAll) {
return "ACCEPT ALL";
}
StringJoiner sb = new StringJoiner(", ");
for (String key : thresholds.keySet().toArray(new String[0])) {
Long value = thresholds.get(key);
sb.add(key + " = " + value);
}
return sb.toString();
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -30,61 +30,82 @@ import java.io.EOFException; ...@@ -30,61 +30,82 @@ import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.charset.Charset; import java.nio.file.Path;
public final class RecordingInput implements DataInput, AutoCloseable { public final class RecordingInput implements DataInput, AutoCloseable {
public static final byte STRING_ENCODING_NULL = 0; private final static int DEFAULT_BLOCK_SIZE = 64_000;
public static final byte STRING_ENCODING_EMPTY_STRING = 1;
public static final byte STRING_ENCODING_CONSTANT_POOL = 2;
public static final byte STRING_ENCODING_UTF8_BYTE_ARRAY = 3;
public static final byte STRING_ENCODING_CHAR_ARRAY = 4;
public static final byte STRING_ENCODING_LATIN1_BYTE_ARRAY = 5;
private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024;
private final static Charset UTF8 = Charset.forName("UTF-8");
private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
private static final class Block { private static final class Block {
private byte[] bytes = new byte[0]; private byte[] bytes = new byte[0];
private long blockPosition; private long blockPosition;
private long blockPositionEnd;
boolean contains(long position) { boolean contains(long position) {
return position >= blockPosition && position < blockPosition + bytes.length; return position >= blockPosition && position < blockPositionEnd;
} }
public void read(RandomAccessFile file, int amount) throws IOException { public void read(RandomAccessFile file, int amount) throws IOException {
blockPosition = file.getFilePointer(); blockPosition = file.getFilePointer();
// reuse byte array, if possible // reuse byte array, if possible
if (amount != bytes.length) { if (amount > bytes.length) {
bytes = new byte[amount]; bytes = new byte[amount];
} }
file.readFully(bytes); this.blockPositionEnd = blockPosition + amount;
file.readFully(bytes, 0, amount);
} }
public byte get(long position) { public byte get(long position) {
return bytes[(int) (position - blockPosition)]; return bytes[(int) (position - blockPosition)];
} }
}
private final RandomAccessFile file; public void reset() {
private final long size; blockPosition = 0;
blockPositionEnd = 0;
}
}
private final int blockSize;
private final FileAccess fileAccess;
private RandomAccessFile file;
private String filename;
private Block currentBlock = new Block(); private Block currentBlock = new Block();
private Block previousBlock = new Block(); private Block previousBlock = new Block();
private long position; private long position;
private final int blockSize; private long size = -1; // Fail fast if setSize(...) has not been called
// before parsing
private RecordingInput(File f, int blockSize) throws IOException { RecordingInput(File f, FileAccess fileAccess, int blockSize) throws IOException {
this.size = f.length();
this.blockSize = blockSize; this.blockSize = blockSize;
this.file = new RandomAccessFile(f, "r"); this.fileAccess = fileAccess;
if (size < 8) { initialize(f);
throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes."); }
private void initialize(File f) throws IOException {
this.filename = fileAccess.getAbsolutePath(f);
this.file = fileAccess.openRAF(f, "r");
this.position = 0;
this.size = -1;
this.currentBlock.reset();
previousBlock.reset();
if (fileAccess.length(f) < 8) {
throw new IOException("Not a valid Flight Recorder file. File length is only " + fileAccess.length(f) + " bytes.");
} }
} }
public RecordingInput(File f) throws IOException { public RecordingInput(File f, FileAccess fileAccess) throws IOException {
this(f, DEFAULT_BLOCK_SIZE); this(f, fileAccess, DEFAULT_BLOCK_SIZE);
}
void positionPhysical(long position) throws IOException {
file.seek(position);
}
byte readPhysicalByte() throws IOException {
return file.readByte();
}
long readPhysicalLong() throws IOException {
return file.readLong();
} }
@Override @Override
...@@ -109,7 +130,7 @@ public final class RecordingInput implements DataInput, AutoCloseable { ...@@ -109,7 +130,7 @@ public final class RecordingInput implements DataInput, AutoCloseable {
readFully(dst, 0, dst.length); readFully(dst, 0, dst.length);
} }
public final short readRawShort() throws IOException { short readRawShort() throws IOException {
// copied from java.io.Bits // copied from java.io.Bits
byte b0 = readByte(); byte b0 = readByte();
byte b1 = readByte(); byte b1 = readByte();
...@@ -117,18 +138,18 @@ public final class RecordingInput implements DataInput, AutoCloseable { ...@@ -117,18 +138,18 @@ public final class RecordingInput implements DataInput, AutoCloseable {
} }
@Override @Override
public final double readDouble() throws IOException { public double readDouble() throws IOException {
// copied from java.io.Bits // copied from java.io.Bits
return Double.longBitsToDouble(readRawLong()); return Double.longBitsToDouble(readRawLong());
} }
@Override @Override
public final float readFloat() throws IOException { public float readFloat() throws IOException {
// copied from java.io.Bits // copied from java.io.Bits
return Float.intBitsToFloat(readRawInt()); return Float.intBitsToFloat(readRawInt());
} }
public final int readRawInt() throws IOException { int readRawInt() throws IOException {
// copied from java.io.Bits // copied from java.io.Bits
byte b0 = readByte(); byte b0 = readByte();
byte b1 = readByte(); byte b1 = readByte();
...@@ -137,7 +158,7 @@ public final class RecordingInput implements DataInput, AutoCloseable { ...@@ -137,7 +158,7 @@ public final class RecordingInput implements DataInput, AutoCloseable {
return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24); return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24);
} }
public final long readRawLong() throws IOException { long readRawLong() throws IOException {
// copied from java.io.Bits // copied from java.io.Bits
byte b0 = readByte(); byte b0 = readByte();
byte b1 = readByte(); byte b1 = readByte();
...@@ -150,20 +171,20 @@ public final class RecordingInput implements DataInput, AutoCloseable { ...@@ -150,20 +171,20 @@ public final class RecordingInput implements DataInput, AutoCloseable {
return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56); return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56);
} }
public final long position() throws IOException { public final long position() {
return position; return position;
} }
public final void position(long newPosition) throws IOException { public final void position(long newPosition) throws IOException {
if (!currentBlock.contains(newPosition)) { if (!currentBlock.contains(newPosition)) {
if (!previousBlock.contains(newPosition)) { if (!previousBlock.contains(newPosition)) {
if (newPosition > size()) { if (newPosition > size) {
throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes."); throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size + " bytes.");
} }
long blockStart = trimToFileSize(calculateBlockStart(newPosition)); long blockStart = trimToFileSize(calculateBlockStart(newPosition));
file.seek(blockStart); file.seek(blockStart);
// trim amount to file size // trim amount to file size
long amount = Math.min(size() - blockStart, blockSize); long amount = Math.min(size - blockStart, blockSize);
previousBlock.read(file, (int) amount); previousBlock.read(file, (int) amount);
} }
// swap previous and current // swap previous and current
...@@ -191,11 +212,12 @@ public final class RecordingInput implements DataInput, AutoCloseable { ...@@ -191,11 +212,12 @@ public final class RecordingInput implements DataInput, AutoCloseable {
return newPosition - blockSize / 2; return newPosition - blockSize / 2;
} }
public final long size() throws IOException { long size() {
return size; return size;
} }
public final void close() throws IOException { @Override
public void close() throws IOException {
file.close(); file.close();
} }
...@@ -245,34 +267,7 @@ public final class RecordingInput implements DataInput, AutoCloseable { ...@@ -245,34 +267,7 @@ public final class RecordingInput implements DataInput, AutoCloseable {
// 4, means "" // 4, means ""
@Override @Override
public String readUTF() throws IOException { public String readUTF() throws IOException {
return readEncodedString(readByte()); throw new UnsupportedOperationException("Use StringParser");
}
public String readEncodedString(byte encoding) throws IOException {
if (encoding == STRING_ENCODING_NULL) {
return null;
}
if (encoding == STRING_ENCODING_EMPTY_STRING) {
return "";
}
int size = readInt();
if (encoding == STRING_ENCODING_CHAR_ARRAY) {
char[] c = new char[size];
for (int i = 0; i < size; i++) {
c[i] = readChar();
}
return new String(c);
}
byte[] bytes = new byte[size];
readFully(bytes); // TODO: optimize, check size, and copy only if needed
if (encoding == STRING_ENCODING_UTF8_BYTE_ARRAY) {
return new String(bytes, UTF8);
}
if (encoding == STRING_ENCODING_LATIN1_BYTE_ARRAY) {
return new String(bytes, LATIN1);
}
throw new IOException("Unknown string encoding " + encoding);
} }
@Override @Override
...@@ -292,48 +287,152 @@ public final class RecordingInput implements DataInput, AutoCloseable { ...@@ -292,48 +287,152 @@ public final class RecordingInput implements DataInput, AutoCloseable {
@Override @Override
public long readLong() throws IOException { public long readLong() throws IOException {
// can be optimized by branching checks, but will do for now final byte[] bytes = currentBlock.bytes;
final int index = (int) (position - currentBlock.blockPosition);
if (index + 8 < bytes.length && index >= 0) {
byte b0 = bytes[index];
long ret = (b0 & 0x7FL);
if (b0 >= 0) {
position += 1;
return ret;
}
int b1 = bytes[index + 1];
ret += (b1 & 0x7FL) << 7;
if (b1 >= 0) {
position += 2;
return ret;
}
int b2 = bytes[index + 2];
ret += (b2 & 0x7FL) << 14;
if (b2 >= 0) {
position += 3;
return ret;
}
int b3 = bytes[index + 3];
ret += (b3 & 0x7FL) << 21;
if (b3 >= 0) {
position += 4;
return ret;
}
int b4 = bytes[index + 4];
ret += (b4 & 0x7FL) << 28;
if (b4 >= 0) {
position += 5;
return ret;
}
int b5 = bytes[index + 5];
ret += (b5 & 0x7FL) << 35;
if (b5 >= 0) {
position += 6;
return ret;
}
int b6 = bytes[index + 6];
ret += (b6 & 0x7FL) << 42;
if (b6 >= 0) {
position += 7;
return ret;
}
int b7 = bytes[index + 7];
ret += (b7 & 0x7FL) << 49;
if (b7 >= 0) {
position += 8;
return ret;
}
int b8 = bytes[index + 8];// read last byte raw
position += 9;
return ret + (((long) (b8 & 0XFF)) << 56);
} else {
return readLongSlow();
}
}
private long readLongSlow() throws IOException {
byte b0 = readByte(); byte b0 = readByte();
long ret = (b0 & 0x7FL); long ret = (b0 & 0x7FL);
if (b0 >= 0) { if (b0 >= 0) {
return ret; return ret;
} }
int b1 = readByte(); int b1 = readByte();
ret += (b1 & 0x7FL) << 7; ret += (b1 & 0x7FL) << 7;
if (b1 >= 0) { if (b1 >= 0) {
return ret; return ret;
} }
int b2 = readByte(); int b2 = readByte();
ret += (b2 & 0x7FL) << 14; ret += (b2 & 0x7FL) << 14;
if (b2 >= 0) { if (b2 >= 0) {
return ret; return ret;
} }
int b3 = readByte(); int b3 = readByte();
ret += (b3 & 0x7FL) << 21; ret += (b3 & 0x7FL) << 21;
if (b3 >= 0) { if (b3 >= 0) {
return ret; return ret;
} }
int b4 = readByte(); int b4 = readByte();
ret += (b4 & 0x7FL) << 28; ret += (b4 & 0x7FL) << 28;
if (b4 >= 0) { if (b4 >= 0) {
return ret; return ret;
} }
int b5 = readByte(); int b5 = readByte();
ret += (b5 & 0x7FL) << 35; ret += (b5 & 0x7FL) << 35;
if (b5 >= 0) { if (b5 >= 0) {
return ret; return ret;
} }
int b6 = readByte(); int b6 = readByte();
ret += (b6 & 0x7FL) << 42; ret += (b6 & 0x7FL) << 42;
if (b6 >= 0) { if (b6 >= 0) {
return ret; return ret;
} }
int b7 = readByte(); int b7 = readByte();
ret += (b7 & 0x7FL) << 49; ret += (b7 & 0x7FL) << 49;
if (b7 >= 0) { if (b7 >= 0) {
return ret; return ret;
} }
int b8 = readByte(); // read last byte raw int b8 = readByte(); // read last byte raw
return ret + (((long) (b8 & 0XFF)) << 56); return ret + (((long) (b8 & 0XFF)) << 56);
} }
public void setValidSize(long size) {
if (size > this.size) {
this.size = size;
}
}
public long getFileSize() throws IOException {
return file.length();
}
public String getFilename() {
return filename;
}
// Purpose of this method is to reuse block cache from a
// previous RecordingInput
public void setFile(Path path) throws IOException {
try {
file.close();
} catch (IOException e) {
// perhaps deleted
}
file = null;
initialize(path.toFile());
}
/*
*
*
*/
} }
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath;
public final class RepositoryFiles {
private static final Object WAIT_OBJECT = new Object();
public static void notifyNewFile() {
synchronized (WAIT_OBJECT) {
WAIT_OBJECT.notifyAll();
}
}
private final FileAccess fileAccess;
private final NavigableMap<Long, Path> pathSet = new TreeMap<>();
private final Map<Path, Long> pathLookup = new HashMap<>();
private final Path repository;
private final Object waitObject;
private volatile boolean closed;
RepositoryFiles(FileAccess fileAccess, Path repository) {
this.repository = repository;
this.fileAccess = fileAccess;
this.waitObject = repository == null ? WAIT_OBJECT : new Object();
}
long getTimestamp(Path p) {
return pathLookup.get(p);
}
Path lastPath() {
if (waitForPaths()) {
return pathSet.lastEntry().getValue();
}
return null; // closed
}
Path firstPath(long startTimeNanos) {
if (waitForPaths()) {
// Pick closest chunk before timestamp
Long time = pathSet.floorKey(startTimeNanos);
if (time != null) {
startTimeNanos = time;
}
return path(startTimeNanos);
}
return null; // closed
}
private boolean waitForPaths() {
while (!closed) {
try {
if (updatePaths()) {
break;
}
} catch (IOException e) {
Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "IOException during repository file scan " + e.getMessage());
// This can happen if a chunk is being removed
// between the file was discovered and an instance
// was accessed, or if new file has been written yet
// Just ignore, and retry later.
}
nap();
}
return !closed;
}
Path nextPath(long startTimeNanos) {
if (closed) {
return null;
}
// Try to get the 'exact' path first
// to avoid skipping files if repository
// is updated while DirectoryStream
// is traversing it
Path path = pathSet.get(startTimeNanos);
if (path != null) {
return path;
}
// Update paths
try {
updatePaths();
} catch (IOException e) {
// ignore
}
// try to get the next file
return path(startTimeNanos);
}
private Path path(long timestamp) {
if (closed) {
return null;
}
while (true) {
SortedMap<Long, Path> after = pathSet.tailMap(timestamp);
if (!after.isEmpty()) {
Path path = after.get(after.firstKey());
if (Logger.shouldLog(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE)) {
Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE, "Return path " + path + " for start time nanos " + timestamp);
}
return path;
}
if (!waitForPaths()) {
return null; // closed
}
}
}
private void nap() {
try {
synchronized (waitObject) {
waitObject.wait(1000);
}
} catch (InterruptedException e) {
// ignore
}
}
private boolean updatePaths() throws IOException {
boolean foundNew = false;
Path repoPath = repository;
if (repoPath == null) {
// Always get the latest repository if 'jcmd JFR.configure
// repositorypath=...' has been executed
SafePath sf = Repository.getRepository().getRepositoryPath();
if (sf == null) {
return false; // not initialized
}
repoPath = sf.toPath();
}
try (DirectoryStream<Path> dirStream = fileAccess.newDirectoryStream(repoPath)) {
List<Path> added = new ArrayList<>();
Set<Path> current = new HashSet<>();
for (Path p : dirStream) {
if (!pathLookup.containsKey(p)) {
String s = p.toString();
if (s.endsWith(".jfr")) {
added.add(p);
Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "New file found: " + p.toAbsolutePath());
}
current.add(p);
}
}
List<Path> removed = new ArrayList<>();
for (Path p : pathLookup.keySet()) {
if (!current.contains(p)) {
removed.add(p);
}
}
for (Path remove : removed) {
Long time = pathLookup.get(remove);
pathSet.remove(time);
pathLookup.remove(remove);
}
Collections.sort(added, (p1, p2) -> p1.compareTo(p2));
for (Path p : added) {
// Only add files that have a complete header
// as the JVM may be in progress writing the file
long size = fileAccess.fileSize(p);
if (size >= ChunkHeader.headerSize()) {
long startNanos = readStartTime(p);
pathSet.put(startNanos, p);
pathLookup.put(p, startNanos);
foundNew = true;
}
}
return foundNew;
}
}
private long readStartTime(Path p) throws IOException {
try (RecordingInput in = new RecordingInput(p.toFile(), fileAccess, 100)) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Parsing header for chunk start time");
ChunkHeader c = new ChunkHeader(in);
return c.getStartNanos();
}
}
void close() {
synchronized (waitObject) {
this.closed = true;
waitObject.notify();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.Utils;
import jdk.jfr.internal.consumer.Dispatcher.EventDispatcher;
final class StreamConfiguration {
final List<Runnable> closeActions = new ArrayList<>();
final List<Runnable> flushActions = new ArrayList<>();
final List<EventDispatcher> eventActions = new ArrayList<>();
final List<Consumer<Throwable>> errorActions = new ArrayList<>();
boolean reuse = true;
boolean ordered = true;
Instant startTime = null;
Instant endTime = null;
boolean started = false;
long startNanos = 0;
long endNanos = Long.MAX_VALUE;
private volatile boolean changed = true;
public synchronized boolean remove(Object action) {
boolean removed = false;
removed |= flushActions.removeIf(e -> e == action);
removed |= closeActions.removeIf(e -> e == action);
removed |= errorActions.removeIf(e -> e == action);
removed |= eventActions.removeIf(e -> e.getAction() == action);
if (removed) {
changed = true;
}
return removed;
}
public synchronized void addEventAction(String name, Consumer<RecordedEvent> consumer) {
eventActions.add(new EventDispatcher(name, consumer));
changed = true;
}
public void addEventAction(Consumer<RecordedEvent> action) {
addEventAction(null, action);
}
public synchronized void addFlushAction(Runnable action) {
flushActions.add(action);
changed = true;
}
public synchronized void addCloseAction(Runnable action) {
closeActions.add(action);
changed = true;
}
public synchronized void addErrorAction(Consumer<Throwable> action) {
errorActions.add(action);
changed = true;
}
public synchronized void setReuse(boolean reuse) {
this.reuse = reuse;
changed = true;
}
public synchronized void setOrdered(boolean ordered) {
this.ordered = ordered;
changed = true;
}
public synchronized void setEndTime(Instant endTime) {
this.endTime = endTime;
this.endNanos = Utils.timeToNanos(endTime);
changed = true;
}
public synchronized void setStartTime(Instant startTime) {
this.startTime = startTime;
this.startNanos = Utils.timeToNanos(startTime);
changed = true;
}
public synchronized void setStartNanos(long startNanos) {
this.startNanos = startNanos;
changed = true;
}
public synchronized void setStarted(boolean started) {
this.started = started;
changed = true;
}
public boolean hasChanged() {
return changed;
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.consumer;
import java.io.IOException;
import java.nio.charset.Charset;
public final class StringParser extends Parser {
public enum Encoding {
NULL(0),
EMPTY_STRING(1),
CONSTANT_POOL(2),
UT8_BYTE_ARRAY(3),
CHAR_ARRAY(4),
LATIN1_BYTE_ARRAY(5);
private byte byteValue;
private Encoding(int byteValue) {
this.byteValue = (byte) byteValue;
}
public byte byteValue() {
return byteValue;
}
public boolean is(byte value) {
return value == byteValue;
}
}
private final static Charset UTF8 = Charset.forName("UTF-8");
private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
private final static class CharsetParser extends Parser {
private final Charset charset;
private int lastSize;
private byte[] buffer = new byte[16];
private String lastString;
CharsetParser(Charset charset) {
this.charset = charset;
}
@Override
public Object parse(RecordingInput input) throws IOException {
int size = input.readInt();
ensureSize(size);
if (lastSize == size) {
boolean equalsLastString = true;
for (int i = 0; i < size; i++) {
// TODO: No need to read byte per byte
byte b = input.readByte();
if (buffer[i] != b) {
equalsLastString = false;
buffer[i] = b;
}
}
if (equalsLastString) {
return lastString;
}
} else {
for (int i = 0; i < size; i++) {
buffer[i] = input.readByte();
}
}
lastString = new String(buffer, 0, size, charset);
lastSize = size;
return lastString;
}
@Override
public void skip(RecordingInput input) throws IOException {
int size = input.readInt();
input.skipBytes(size);
}
private void ensureSize(int size) {
if (buffer.length < size) {
buffer = new byte[size];
}
}
}
private final static class CharArrayParser extends Parser {
private char[] buffer = new char[16];
private int lastSize = -1;
private String lastString = null;
@Override
public Object parse(RecordingInput input) throws IOException {
int size = input.readInt();
ensureSize(size);
if (lastSize == size) {
boolean equalsLastString = true;
for (int i = 0; i < size; i++) {
char c = input.readChar();
if (buffer[i] != c) {
equalsLastString = false;
buffer[i] = c;
}
}
if (equalsLastString) {
return lastString;
}
} else {
for (int i = 0; i < size; i++) {
buffer[i] = input.readChar();
}
}
lastString = new String(buffer, 0, size);
lastSize = size;
return lastString;
}
@Override
public void skip(RecordingInput input) throws IOException {
int size = input.readInt();
for (int i = 0; i < size; i++) {
input.readChar();
}
}
private void ensureSize(int size) {
if (buffer.length < size) {
buffer = new char[size];
}
}
}
private final ConstantLookup stringLookup;
private final CharArrayParser charArrayParser = new CharArrayParser();
private final CharsetParser utf8parser = new CharsetParser(UTF8);
private final CharsetParser latin1parser = new CharsetParser(LATIN1);
private final boolean event;
public StringParser(ConstantLookup stringLookup, boolean event) {
this.stringLookup = stringLookup;
this.event = event;
}
@Override
public Object parse(RecordingInput input) throws IOException {
byte encoding = input.readByte();
if (Encoding.CONSTANT_POOL.is(encoding)) {
long key = input.readLong();
if (event) {
return stringLookup.getCurrentResolved(key);
} else {
return stringLookup.getCurrent(key);
}
}
if (Encoding.NULL.is(encoding)) {
return null;
}
if (Encoding.EMPTY_STRING.is(encoding)) {
return "";
}
if (Encoding.CHAR_ARRAY.is(encoding)) {
return charArrayParser.parse(input);
}
if (Encoding.UT8_BYTE_ARRAY.is(encoding)) {
return utf8parser.parse(input);
}
if (Encoding.LATIN1_BYTE_ARRAY.is(encoding)) {
return latin1parser.parse(input);
}
throw new IOException("Unknown string encoding " + encoding);
}
@Override
public void skip(RecordingInput input) throws IOException {
byte encoding = input.readByte();
if (Encoding.CONSTANT_POOL.is(encoding)) {
input.readLong();
return;
}
if (Encoding.EMPTY_STRING.is(encoding)) {
return;
}
if (Encoding.NULL.is(encoding)) {
return;
}
if (Encoding.CHAR_ARRAY.is(encoding)) {
charArrayParser.skip(input);
return;
}
if (Encoding.UT8_BYTE_ARRAY.is(encoding)) {
utf8parser.skip(input);
return;
}
if (Encoding.LATIN1_BYTE_ARRAY.is(encoding)) {
latin1parser.skip(input);
return;
}
throw new IOException("Unknown string encoding " + encoding);
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
* questions. * questions.
*/ */
package jdk.jfr.consumer; package jdk.jfr.internal.consumer;
import java.time.DateTimeException; import java.time.DateTimeException;
import java.time.ZoneOffset; import java.time.ZoneOffset;
...@@ -49,15 +49,6 @@ final class TimeConverter { ...@@ -49,15 +49,6 @@ final class TimeConverter {
this.zoneOffet = zoneOfSet(rawOffset); this.zoneOffet = zoneOfSet(rawOffset);
} }
private ZoneOffset zoneOfSet(int rawOffset) {
try {
return ZoneOffset.ofTotalSeconds(rawOffset / 1000);
} catch (DateTimeException dte) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Could not create ZoneOffset from raw offset " + rawOffset);
}
return ZoneOffset.UTC;
}
public long convertTimestamp(long ticks) { public long convertTimestamp(long ticks) {
return startNanos + (long) ((ticks - startTicks) / divisor); return startNanos + (long) ((ticks - startTicks) / divisor);
} }
...@@ -69,4 +60,13 @@ final class TimeConverter { ...@@ -69,4 +60,13 @@ final class TimeConverter {
public ZoneOffset getZoneOffset() { public ZoneOffset getZoneOffset() {
return zoneOffet; return zoneOffet;
} }
private ZoneOffset zoneOfSet(int rawOffset) {
try {
return ZoneOffset.ofTotalSeconds(rawOffset / 1000);
} catch (DateTimeException dte) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Could not create ZoneOffset from raw offset " + rawOffset);
}
return ZoneOffset.UTC;
}
} }
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -27,10 +27,12 @@ package jdk.jfr.internal.dcmd; ...@@ -27,10 +27,12 @@ package jdk.jfr.internal.dcmd;
import jdk.jfr.FlightRecorder;
import jdk.jfr.internal.LogLevel; import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag; import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger; import jdk.jfr.internal.Logger;
import jdk.jfr.internal.Options; import jdk.jfr.internal.Options;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Repository; import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath; import jdk.jfr.internal.SecuritySupport.SafePath;
...@@ -101,6 +103,9 @@ final class DCmdConfigure extends AbstractDCmd { ...@@ -101,6 +103,9 @@ final class DCmdConfigure extends AbstractDCmd {
SafePath s = new SafePath(repositoryPath); SafePath s = new SafePath(repositoryPath);
Repository.getRepository().setBasePath(s); Repository.getRepository().setBasePath(s);
Logger.log(LogTag.JFR, LogLevel.INFO, "Base repository path set to " + repositoryPath); Logger.log(LogTag.JFR, LogLevel.INFO, "Base repository path set to " + repositoryPath);
if (FlightRecorder.isInitialized()) {
PrivateAccess.getInstance().getPlatformRecorder().rotateIfRecordingToDisk();;
}
} catch (Exception e) { } catch (Exception e) {
throw new DCmdException("Could not use " + repositoryPath + " as repository. " + e.getMessage(), e); throw new DCmdException("Could not use " + repositoryPath + " as repository. " + e.getMessage(), e);
} }
......
/* /*
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -83,7 +83,7 @@ final class DCmdStart extends AbstractDCmd { ...@@ -83,7 +83,7 @@ final class DCmdStart extends AbstractDCmd {
* @throws DCmdException if recording could not be started * @throws DCmdException if recording could not be started
*/ */
@SuppressWarnings("resource") @SuppressWarnings("resource")
public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException { public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Long flush, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) { if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name + Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name +
", settings=" + (settings != null ? Arrays.asList(settings) : "(none)") + ", settings=" + (settings != null ? Arrays.asList(settings) : "(none)") +
...@@ -92,6 +92,7 @@ final class DCmdStart extends AbstractDCmd { ...@@ -92,6 +92,7 @@ final class DCmdStart extends AbstractDCmd {
", disk=" + disk+ ", disk=" + disk+
", filename=" + path + ", filename=" + path +
", maxage=" + maxAge + ", maxage=" + maxAge +
", flush=" + flush +
", maxsize=" + maxSize + ", maxsize=" + maxSize +
", dumponexit =" + dumpOnExit + ", dumponexit =" + dumpOnExit +
", path-to-gc-roots=" + pathToGcRoots); ", path-to-gc-roots=" + pathToGcRoots);
...@@ -138,6 +139,12 @@ final class DCmdStart extends AbstractDCmd { ...@@ -138,6 +139,12 @@ final class DCmdStart extends AbstractDCmd {
} }
} }
if (flush != null) {
if (Boolean.FALSE.equals(disk)) {
throw new DCmdException("Flush can only be set for recordings that are to disk.");
}
}
if (!FlightRecorder.isInitialized() && delay == null) { if (!FlightRecorder.isInitialized() && delay == null) {
initializeWithForcedInstrumentation(s); initializeWithForcedInstrumentation(s);
} }
...@@ -150,6 +157,7 @@ final class DCmdStart extends AbstractDCmd { ...@@ -150,6 +157,7 @@ final class DCmdStart extends AbstractDCmd {
if (disk != null) { if (disk != null) {
recording.setToDisk(disk.booleanValue()); recording.setToDisk(disk.booleanValue());
} }
recording.setSettings(s); recording.setSettings(s);
SafePath safePath = null; SafePath safePath = null;
...@@ -187,6 +195,10 @@ final class DCmdStart extends AbstractDCmd { ...@@ -187,6 +195,10 @@ final class DCmdStart extends AbstractDCmd {
recording.setMaxAge(Duration.ofNanos(maxAge)); recording.setMaxAge(Duration.ofNanos(maxAge));
} }
if (flush != null) {
recording.setFlushInterval(Duration.ofNanos(flush));
}
if (maxSize != null) { if (maxSize != null) {
recording.setMaxSize(maxSize); recording.setMaxSize(maxSize);
} }
...@@ -232,6 +244,7 @@ final class DCmdStart extends AbstractDCmd { ...@@ -232,6 +244,7 @@ final class DCmdStart extends AbstractDCmd {
print("Use jcmd " + getPid() + " JFR." + cmd + " " + recordingspecifier + " " + fileOption + "to copy recording data to file."); print("Use jcmd " + getPid() + " JFR." + cmd + " " + recordingspecifier + " " + fileOption + "to copy recording data to file.");
println(); println();
} }
return getResult(); return getResult();
} }
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -41,6 +41,7 @@ import java.util.Deque; ...@@ -41,6 +41,7 @@ import java.util.Deque;
import java.util.List; import java.util.List;
import jdk.jfr.internal.consumer.ChunkHeader; import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput; import jdk.jfr.internal.consumer.RecordingInput;
final class Disassemble extends Command { final class Disassemble extends Command {
...@@ -163,7 +164,7 @@ final class Disassemble extends Command { ...@@ -163,7 +164,7 @@ final class Disassemble extends Command {
} }
private List<Long> findChunkSizes(Path p) throws IOException { private List<Long> findChunkSizes(Path p) throws IOException {
try (RecordingInput input = new RecordingInput(p.toFile())) { try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILIGED)) {
List<Long> sizes = new ArrayList<>(); List<Long> sizes = new ArrayList<>();
ChunkHeader ch = new ChunkHeader(input); ChunkHeader ch = new ChunkHeader(input);
sizes.add(ch.getSize()); sizes.add(ch.getSize());
......
...@@ -30,6 +30,7 @@ import java.io.IOException; ...@@ -30,6 +30,7 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -42,7 +43,7 @@ import jdk.jfr.ValueDescriptor; ...@@ -42,7 +43,7 @@ import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedObject; import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordingFile; import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.consumer.RecordingInternals; import jdk.jfr.internal.consumer.JdkJfrConsumer;
abstract class EventPrintWriter extends StructuredWriter { abstract class EventPrintWriter extends StructuredWriter {
...@@ -52,6 +53,7 @@ abstract class EventPrintWriter extends StructuredWriter { ...@@ -52,6 +53,7 @@ abstract class EventPrintWriter extends StructuredWriter {
protected static final String STACK_TRACE_FIELD = "stackTrace"; protected static final String STACK_TRACE_FIELD = "stackTrace";
protected static final String EVENT_THREAD_FIELD = "eventThread"; protected static final String EVENT_THREAD_FIELD = "eventThread";
private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
private Predicate<EventType> eventFilter = x -> true; private Predicate<EventType> eventFilter = x -> true;
private int stackDepth; private int stackDepth;
...@@ -74,8 +76,8 @@ abstract class EventPrintWriter extends StructuredWriter { ...@@ -74,8 +76,8 @@ abstract class EventPrintWriter extends StructuredWriter {
if (acceptEvent(event)) { if (acceptEvent(event)) {
events.add(event); events.add(event);
} }
if (RecordingInternals.INSTANCE.isLastEventInChunk(file)) { if (PRIVATE_ACCESS.isLastEventInChunk(file)) {
RecordingInternals.INSTANCE.sort(events); Collections.sort(events, PRIVATE_ACCESS.eventComparator());
print(events); print(events);
events.clear(); events.clear();
} }
...@@ -121,7 +123,7 @@ abstract class EventPrintWriter extends StructuredWriter { ...@@ -121,7 +123,7 @@ abstract class EventPrintWriter extends StructuredWriter {
case TIMESPAN: case TIMESPAN:
return object.getDuration(v.getName()); return object.getDuration(v.getName());
case TIMESTAMP: case TIMESTAMP:
return RecordingInternals.INSTANCE.getOffsetDataTime(object, v.getName()); return PRIVATE_ACCESS.getOffsetDataTime(object, v.getName());
default: default:
return object.getValue(v.getName()); return object.getValue(v.getName());
} }
......
...@@ -35,10 +35,12 @@ import java.util.List; ...@@ -35,10 +35,12 @@ import java.util.List;
import jdk.jfr.consumer.RecordingFile; import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.Type; import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.RecordingInternals; import jdk.jfr.internal.consumer.JdkJfrConsumer;
final class Metadata extends Command { final class Metadata extends Command {
private final static JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
private static class TypeComparator implements Comparator<Type> { private static class TypeComparator implements Comparator<Type> {
@Override @Override
...@@ -89,6 +91,7 @@ final class Metadata extends Command { ...@@ -89,6 +91,7 @@ final class Metadata extends Command {
} }
} }
@Override @Override
public String getName() { public String getName() {
return "metadata"; return "metadata";
...@@ -125,7 +128,7 @@ final class Metadata extends Command { ...@@ -125,7 +128,7 @@ final class Metadata extends Command {
PrettyWriter prettyWriter = new PrettyWriter(pw); PrettyWriter prettyWriter = new PrettyWriter(pw);
prettyWriter.setShowIds(showIds); prettyWriter.setShowIds(showIds);
try (RecordingFile rf = new RecordingFile(file)) { try (RecordingFile rf = new RecordingFile(file)) {
List<Type> types = RecordingInternals.INSTANCE.readTypes(rf); List<Type> types = PRIVATE_ACCESS.readTypes(rf);
Collections.sort(types, new TypeComparator()); Collections.sort(types, new TypeComparator());
for (Type type : types) { for (Type type : types) {
prettyWriter.printType(type); prettyWriter.printType(type);
......
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -42,6 +42,7 @@ import jdk.jfr.EventType; ...@@ -42,6 +42,7 @@ import jdk.jfr.EventType;
import jdk.jfr.internal.MetadataDescriptor; import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type; import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader; import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput; import jdk.jfr.internal.consumer.RecordingInput;
final class Summary extends Command { final class Summary extends Command {
...@@ -91,7 +92,7 @@ final class Summary extends Command { ...@@ -91,7 +92,7 @@ final class Summary extends Command {
long totalDuration = 0; long totalDuration = 0;
long chunks = 0; long chunks = 0;
try (RecordingInput input = new RecordingInput(p.toFile())) { try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILIGED)) {
ChunkHeader first = new ChunkHeader(input); ChunkHeader first = new ChunkHeader(input);
ChunkHeader ch = first; ChunkHeader ch = first;
String eventPrefix = Type.EVENT_NAME_PREFIX; String eventPrefix = Type.EVENT_NAME_PREFIX;
......
/* /*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
package jdk.jfr.api.consumer; package jdk.jfr.api.consumer;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
......
/* /*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -210,12 +210,12 @@ public class TestRecordingFile { ...@@ -210,12 +210,12 @@ public class TestRecordingFile {
assertHasEventType(types, "Event2"); assertHasEventType(types, "Event2");
assertMissingEventType(types, "Event3"); assertMissingEventType(types, "Event3");
} }
try (RecordingFile f = new RecordingFile(twoEventTypes)) { try (RecordingFile f = new RecordingFile(threeEventTypes)) {
List<EventType> types = f.readEventTypes(); List<EventType> types = f.readEventTypes();
assertUniqueEventTypes(types); assertUniqueEventTypes(types);
assertHasEventType(types, "Event1"); assertHasEventType(types, "Event1");
assertHasEventType(types, "Event2"); assertHasEventType(types, "Event2");
assertMissingEventType(types, "Event3"); assertHasEventType(types, "Event3");
} }
} }
......
/* /*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -60,6 +60,7 @@ public class TestRecordingInternals { ...@@ -60,6 +60,7 @@ public class TestRecordingInternals {
Asserts.assertEquals(id.toString(), rt.getThreadGroup().getName(), "Thread group name should match id"); Asserts.assertEquals(id.toString(), rt.getThreadGroup().getName(), "Thread group name should match id");
Asserts.assertEquals(id, Integer.valueOf(i), "Chunks out of order"); Asserts.assertEquals(id, Integer.valueOf(i), "Chunks out of order");
i++; i++;
System.out.println(i + " OK ");
} }
} }
} }
......
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.filestream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicLong;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
/**
* @test
* @summary Verifies that it is possible to stream contents from a multichunked file
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.filestream.TestMultipleChunk
*/
public class TestMultipleChunk {
static class SnakeEvent extends Event {
int id;
}
public static void main(String... args) throws Exception {
Path path = Paths.get("./using-file.jfr");
try (Recording r1 = new Recording()) {
r1.start();
emitSnakeEvent(1);
emitSnakeEvent(2);
emitSnakeEvent(3);
// Force a chunk rotation
try (Recording r2 = new Recording()) {
r2.start();
emitSnakeEvent(4);
emitSnakeEvent(5);
emitSnakeEvent(6);
r2.stop();
}
r1.stop();
r1.dump(path);
AtomicLong counter = new AtomicLong();
try (EventStream es = EventStream.openFile(path)) {
es.onEvent(e -> {
counter.incrementAndGet();
});
es.start();
if (counter.get() != 6) {
throw new Exception("Expected 6 event, but got " + counter.get());
}
}
}
}
static void emitSnakeEvent(int id) {
SnakeEvent e = new SnakeEvent();
e.id = id;
e.commit();
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.filestream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
/**
* @test
* @summary Test EventStream::setOrdered(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.filestream.TestOrdered
*/
public class TestOrdered {
static class OrderedEvent extends Event {
}
static class Emitter extends Thread {
private final CyclicBarrier barrier;
public Emitter(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
OrderedEvent e1 = new OrderedEvent();
e1.commit();
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
throw new Error("Unexpected exception in barrier");
}
OrderedEvent e2 = new OrderedEvent();
e2.commit();
}
}
private static final int THREAD_COUNT = 4;
private static final boolean[] BOOLEAN_STATES = { false, true };
public static void main(String... args) throws Exception {
Path p = makeUnorderedRecording();
testSetOrderedTrue(p);
testSetOrderedFalse(p);
}
private static void testSetOrderedTrue(Path p) throws Exception {
for (boolean reuse : BOOLEAN_STATES) {
AtomicReference<Instant> timestamp = new AtomicReference<>(Instant.MIN);
try (EventStream es = EventStream.openFile(p)) {
es.setReuse(reuse);
es.setOrdered(true);
es.onEvent(e -> {
Instant endTime = e.getEndTime();
if (endTime.isBefore(timestamp.get())) {
throw new Error("Events are not ordered! Reuse = " + reuse);
}
timestamp.set(endTime);
});
es.start();
}
}
}
private static void testSetOrderedFalse(Path p) throws Exception {
for (boolean reuse : BOOLEAN_STATES) {
AtomicReference<Instant> timestamp = new AtomicReference<>(Instant.MIN);
AtomicBoolean unoreded = new AtomicBoolean(false);
try (EventStream es = EventStream.openFile(p)) {
es.setReuse(reuse);
es.setOrdered(false);
es.onEvent(e -> {
Instant endTime = e.getEndTime();
if (endTime.isBefore(timestamp.get())) {
unoreded.set(true);
es.close();
}
timestamp.set(endTime);
});
es.start();
if (!unoreded.get()) {
throw new Exception("Expected at least some events to be out of order! Reuse = " + reuse);
}
}
}
}
private static Path makeUnorderedRecording() throws Exception {
CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
try (Recording r = new Recording()) {
r.start();
List<Emitter> emitters = new ArrayList<>();
for (int i = 0; i < THREAD_COUNT; i++) {
Emitter e = new Emitter(barrier);
e.start();
emitters.add(e);
}
// Thread buffers should now have one event each
barrier.await();
// Add another event to each thread buffer, so
// events are bound to come out of order when they
// are flushed
for (Emitter e : emitters) {
e.join();
}
r.stop();
Path p = Files.createTempFile("recording", ".jfr");
r.dump(p);
return p;
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.filestream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordedEvent;
/**
* @test
* @summary Test EventStream::setReuse(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.filestream.TestReuse
*/
public class TestReuse {
static class ReuseEvent extends Event {
}
private static final boolean[] BOOLEAN_STATES = { false, true };
public static void main(String... args) throws Exception {
Path p = makeRecording();
testSetReuseTrue(p);
testSetReuseFalse(p);
}
private static void testSetReuseFalse(Path p) throws Exception {
for (boolean ordered : BOOLEAN_STATES) {
AtomicBoolean fail = new AtomicBoolean(false);
Map<RecordedEvent, RecordedEvent> identity = new IdentityHashMap<>();
try (EventStream es = EventStream.openFile(p)) {
es.setOrdered(ordered);
es.setReuse(false);
es.onEvent(e -> {
if (identity.containsKey(e)) {
fail.set(true);
es.close();
}
identity.put(e, e);
});
es.start();
}
if (fail.get()) {
throw new Exception("Unexpected reuse! Ordered = " + ordered);
}
}
}
private static void testSetReuseTrue(Path p) throws Exception {
for (boolean ordered : BOOLEAN_STATES) {
AtomicBoolean success = new AtomicBoolean(false);
Map<RecordedEvent, RecordedEvent> events = new IdentityHashMap<>();
try (EventStream es = EventStream.openFile(p)) {
es.setOrdered(ordered);
es.setReuse(true);
es.onEvent(e -> {
if(events.containsKey(e)) {
success.set(true);;
es.close();
}
events.put(e,e);
});
es.start();
}
if (!success.get()) {
throw new Exception("No reuse! Ordered = " + ordered);
}
}
}
private static Path makeRecording() throws IOException {
try (Recording r = new Recording()) {
r.start();
for (int i = 0; i < 5; i++) {
ReuseEvent e = new ReuseEvent();
e.commit();
}
Recording rotation = new Recording();
rotation.start();
for (int i = 0; i < 5; i++) {
ReuseEvent e = new ReuseEvent();
e.commit();
}
r.stop();
rotation.close();
Path p = Files.createTempFile("recording", ".jfr");
r.dump(p);
return p;
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import jdk.jfr.api.consumer.recordingstream.TestStart.StartEvent;
class EventProducer extends Thread {
private final Object lock = new Object();
private boolean killed = false;
public void run() {
while (true) {
StartEvent s = new StartEvent();
s.commit();
synchronized (lock) {
try {
lock.wait(10);
if (killed) {
return; // end thread
}
} catch (InterruptedException e) {
// ignore
}
}
}
}
public void kill() {
synchronized (lock) {
this.killed = true;
lock.notifyAll();
}
try {
join();
} catch (InterruptedException e) {
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Test RecordingStream::awaitTermination(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestAwaitTermination
*/
public class TestAwaitTermination {
public static void main(String... args) throws Exception {
testAwaitClose();
testAwaitTimeOut();
}
private static void testAwaitClose() throws InterruptedException, ExecutionException {
try (RecordingStream r = new RecordingStream()) {
r.startAsync();
CompletableFuture<Void> c = CompletableFuture.runAsync(() -> {
try {
r.awaitTermination();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
r.close();
c.get();
}
}
private static void testAwaitTimeOut() throws InterruptedException, ExecutionException {
try (RecordingStream r = new RecordingStream()) {
r.startAsync();
CompletableFuture<Void> c = CompletableFuture.runAsync(() -> {
try {
r.awaitTermination(Duration.ofMillis(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
c.get();
r.close();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::close()
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestClose
*/
public class TestClose {
private static class CloseEvent extends Event {
}
public static void main(String... args) throws Exception {
testCloseUnstarted();
testCloseStarted();
testCloseTwice();
testCloseStreaming();
testCloseMySelf();
}
private static void testCloseMySelf() throws Exception {
log("Entering testCloseMySelf()");
CountDownLatch l1 = new CountDownLatch(1);
CountDownLatch l2 = new CountDownLatch(1);
RecordingStream r = new RecordingStream();
r.onEvent(e -> {
try {
l1.await();
r.close();
l2.countDown();
} catch (InterruptedException ie) {
throw new Error(ie);
}
});
r.startAsync();
CloseEvent c = new CloseEvent();
c.commit();
l1.countDown();
l2.await();
log("Leaving testCloseMySelf()");
}
private static void testCloseStreaming() throws Exception {
log("Entering testCloseStreaming()");
CountDownLatch streaming = new CountDownLatch(1);
RecordingStream r = new RecordingStream();
AtomicLong count = new AtomicLong();
r.onEvent(e -> {
if (count.incrementAndGet() > 100) {
streaming.countDown();
}
});
r.startAsync();
CompletableFuture<Void> streamingLoop = CompletableFuture.runAsync(() -> {
while (true) {
CloseEvent c = new CloseEvent();
c.commit();
}
});
streaming.await();
r.close();
streamingLoop.cancel(true);
log("Leaving testCloseStreaming()");
}
private static void testCloseStarted() {
log("Entering testCloseStarted()");
RecordingStream r = new RecordingStream();
r.startAsync();
r.close();
log("Leaving testCloseStarted()");
}
private static void testCloseUnstarted() {
log("Entering testCloseUnstarted()");
RecordingStream r = new RecordingStream();
r.close();
log("Leaving testCloseUnstarted()");
}
private static void testCloseTwice() {
log("Entering testCloseTwice()");
RecordingStream r = new RecordingStream();
r.startAsync();
r.close();
r.close();
log("Leaving testCloseTwice()");
}
private static void log(String msg) {
System.out.println(msg);
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Configuration;
import jdk.jfr.Enabled;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Tests RecordingStream::RecordingStream()
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestConstructor
*/
public class TestConstructor {
public static void main(String... args) throws Exception {
testEmpty();
testConfiguration();
}
private static void testConfiguration() throws Exception {
CountDownLatch jvmInformation = new CountDownLatch(1);
Configuration c = Configuration.getConfiguration("default");
if (!c.getSettings().containsKey(EventNames.JVMInformation + "#" + Enabled.NAME)) {
throw new Exception("Expected default configuration to contain enabled " + EventNames.JVMInformation);
}
RecordingStream r = new RecordingStream(c);
r.onEvent("jdk.JVMInformation", e -> {
jvmInformation.countDown();
});
r.startAsync();
jvmInformation.await();
r.close();
}
private static void testEmpty() {
RecordingStream r = new RecordingStream();
r.close();
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::disable(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestDisable
*/
public class TestDisable {
private static class DisabledEvent extends Event {
}
private static class EnabledEvent extends Event {
}
public static void main(String... args) throws Exception {
testDisableWithClass();
testDisableWithEventName();
}
private static void testDisableWithEventName() {
test(r -> r.disable(DisabledEvent.class.getName()));
}
private static void testDisableWithClass() {
test(r -> r.disable(DisabledEvent.class));
}
private static void test(Consumer<RecordingStream> disablement) {
CountDownLatch twoEvent = new CountDownLatch(2);
AtomicBoolean fail = new AtomicBoolean(false);
try(RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
if (e.getEventType().getName().equals(DisabledEvent.class.getName())) {
fail.set(true);
}
twoEvent.countDown();
});
disablement.accept(r);
r.startAsync();
EnabledEvent e1 = new EnabledEvent();
e1.commit();
DisabledEvent d1 = new DisabledEvent();
d1.commit();
EnabledEvent e2 = new EnabledEvent();
e2.commit();
try {
twoEvent.await();
} catch (InterruptedException ie) {
throw new RuntimeException("Unexpexpected interruption of thread", ie);
}
if (fail.get()) {
throw new RuntimeException("Should not receive a disabled event");
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import jdk.jfr.Enabled;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::enable(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestEnable
*/
public class TestEnable {
@Enabled(false)
private static class EnabledEvent extends Event {
}
public static void main(String... args) throws Exception {
testEnableWithClass();
testEnableWithEventName();
}
private static void testEnableWithEventName() {
test(r -> r.enable(EnabledEvent.class.getName()));
}
private static void testEnableWithClass() {
test(r -> r.enable(EnabledEvent.class));
}
private static void test(Consumer<RecordingStream> enablement) {
CountDownLatch event = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
event.countDown();
});
enablement.accept(r);
r.startAsync();
EnabledEvent e = new EnabledEvent();
e.commit();
try {
event.await();
} catch (InterruptedException ie) {
throw new RuntimeException("Unexpected interruption of latch", ie);
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.time.Duration;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Tests RecordingStream::setMaxAge(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestMaxAge
*/
public class TestMaxAge {
public static void main(String... args) throws Exception {
Duration testDuration = Duration.ofMillis(1234567);
try (RecordingStream r = new RecordingStream()) {
r.setMaxAge(testDuration);
r.enable(EventNames.ActiveRecording);
r.onEvent(e -> {
System.out.println(e);
Duration d = e.getDuration("maxAge");
System.out.println(d.toMillis());
if (testDuration.equals(d)) {
r.close();
return;
}
System.out.println("Max age not set, was " + d.toMillis() + " ms , but expected " + testDuration.toMillis() + " ms");
});
r.start();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::onClose(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnClose
*/
public class TestOnClose {
private static class CloseEvent extends Event {
}
public static void main(String... args) throws Exception {
testOnCloseNull();
testOnClosedUnstarted();
testOnClosedStarted();
}
private static void testOnCloseNull() {
try (RecordingStream rs = new RecordingStream()) {
try {
rs.onClose(null);
throw new AssertionError("Expected NullPointerException from onClose(null");
} catch (NullPointerException npe) {
// OK; as expected
}
}
}
private static void testOnClosedStarted() throws InterruptedException {
AtomicBoolean onClose = new AtomicBoolean(false);
CountDownLatch event = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
event.countDown();
});
r.onClose(() -> {
onClose.set(true);
});
r.startAsync();
CloseEvent c = new CloseEvent();
c.commit();
event.await();
}
if (!onClose.get()) {
throw new AssertionError("OnClose was not called");
}
}
private static void testOnClosedUnstarted() {
AtomicBoolean onClose = new AtomicBoolean(false);
try (RecordingStream r = new RecordingStream()) {
r.onClose(() -> {
onClose.set(true);
});
}
if (!onClose.get()) {
throw new AssertionError("OnClose was not called");
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.jfr.api.consumer.recordingstream.TestUtils.TestError;
import jdk.jfr.api.consumer.recordingstream.TestUtils.TestException;
import jdk.jfr.api.consumer.security.TestStreamingRemote.TestEvent;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::onError(...) when using
* RecordingStream:startAsync
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnErrorAsync
*/
public class TestOnErrorAsync {
public static void main(String... args) throws Exception {
testDefaultError();
testCustomError();
testDefaultException();
testCustomException();
testOnFlushSanity();
testOnCloseSanity();
}
private static void testDefaultError() throws Exception {
AtomicBoolean closed = new AtomicBoolean();
CountDownLatch receivedError = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
TestError error = new TestError();
TestUtils.installUncaughtException(receivedError, error);
throw error; // closes stream
});
r.onClose(() -> {
closed.set(true);
});
r.startAsync();
TestEvent e = new TestEvent();
e.commit();
r.awaitTermination();
receivedError.await();
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
}
}
private static void testCustomError() throws Exception {
AtomicBoolean onError = new AtomicBoolean();
AtomicBoolean closed = new AtomicBoolean();
CountDownLatch receivedError = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
TestError error = new TestError();
TestUtils.installUncaughtException(receivedError, error);
throw error; // closes stream
});
r.onError(e -> {
onError.set(true);
});
r.onClose(() -> {
closed.set(true);
});
r.startAsync();
TestEvent e = new TestEvent();
e.commit();
r.awaitTermination();
receivedError.await();
if (onError.get()) {
throw new Exception("onError handler should not be invoked on java.lang.Error.");
}
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
}
}
private static void testDefaultException() throws Exception {
TestException exception = new TestException();
AtomicBoolean closed = new AtomicBoolean();
AtomicInteger counter = new AtomicInteger();
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
if (counter.incrementAndGet() == 2) {
r.close();
return;
}
TestUtils.throwUnchecked(exception);
});
r.onClose(() -> {
closed.set(true);
});
r.startAsync();
TestEvent e1 = new TestEvent();
e1.commit();
TestEvent e2 = new TestEvent();
e2.commit();
r.awaitTermination();
if (!exception.isPrinted()) {
throw new Exception("Expected stack trace from Exception to be printed");
}
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
}
}
private static void testCustomException() throws Exception {
TestException exception = new TestException();
AtomicBoolean closed = new AtomicBoolean();
AtomicBoolean received = new AtomicBoolean();
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
TestUtils.throwUnchecked(exception);
});
r.onError(t -> {
received.set(t == exception);
r.close();
});
r.onClose(() -> {
closed.set(true);
});
r.startAsync();
TestEvent event = new TestEvent();
event.commit();
r.awaitTermination();
if (!received.get()) {
throw new Exception("Did not receive expected exception in onError(...)");
}
if (exception.isPrinted()) {
throw new Exception("Expected stack trace from Exception NOT to be printed");
}
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
}
}
private static void testOnFlushSanity() throws Exception {
TestException exception = new TestException();
CountDownLatch received = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onFlush(() -> {
TestUtils.throwUnchecked(exception);
});
r.onError(t -> {
if (t == exception) {
received.countDown();
}
});
r.startAsync();
received.await();
}
}
private static void testOnCloseSanity() throws Exception {
TestException exception = new TestException();
CountDownLatch received = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onFlush(() -> {
r.close(); // will trigger onClose
});
r.onClose(() -> {
TestUtils.throwUnchecked(exception); // will trigger onError
});
r.onError(t -> {
if (t == exception) {
received.countDown();
}
});
r.startAsync();
received.await();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.jfr.api.consumer.recordingstream.TestUtils.TestError;
import jdk.jfr.api.consumer.recordingstream.TestUtils.TestException;
import jdk.jfr.api.consumer.security.TestStreamingRemote.TestEvent;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::onError(...) when using RecordingStream:start
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnErrorSync
*/
public class TestOnErrorSync {
public static void main(String... args) throws Exception {
testDefaultError();
testCustomError();
testDefaultException();
testCustomException();
testOnFlushSanity();
testOnCloseSanity();
}
private static void testDefaultError() throws Exception {
TestError error = new TestError();
AtomicBoolean closed = new AtomicBoolean();
Timer t = newEventEmitter();
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
throw error; // closes stream
});
r.onClose(() -> {
closed.set(true);
});
try {
r.start();
throw new Exception("Expected TestError to be thrown");
} catch (TestError te) {
// as expected
}
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
} finally {
t.cancel();
}
}
private static void testCustomError() throws Exception {
TestError error = new TestError();
AtomicBoolean onError = new AtomicBoolean();
AtomicBoolean closed = new AtomicBoolean();
Timer t = newEventEmitter();
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
throw error; // closes stream
});
r.onError(e -> {
onError.set(true);
});
r.onClose(() -> {
closed.set(true);
});
try {
r.start();
throw new Exception("Expected TestError to be thrown");
} catch (TestError terror) {
// as expected
}
if (onError.get()) {
throw new Exception("Expected onError(...) NOT to be invoked");
}
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
} finally {
t.cancel();
}
}
private static void testDefaultException() throws Exception {
TestException exception = new TestException();
AtomicInteger counter = new AtomicInteger();
AtomicBoolean closed = new AtomicBoolean();
Timer t = newEventEmitter();
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
if (counter.incrementAndGet() == 2) {
// Only close if we get a second event after an exception
r.close();
return;
}
TestUtils.throwUnchecked(exception);
});
r.onClose(() -> {
closed.set(true);
});
try {
r.start();
} catch (Exception e) {
throw new Exception("Unexpected exception thrown from start()", e);
}
if (!exception.isPrinted()) {
throw new Exception("Expected stack trace from Exception to be printed");
}
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
} finally {
t.cancel();
}
}
private static void testCustomException() throws Exception {
TestException exception = new TestException();
AtomicInteger counter = new AtomicInteger();
AtomicBoolean onError = new AtomicBoolean();
AtomicBoolean closed = new AtomicBoolean();
AtomicBoolean received = new AtomicBoolean();
Timer t = newEventEmitter();
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
if (counter.incrementAndGet() == 2) {
// Only close if we get a second event after an exception
r.close();
return;
}
TestUtils.throwUnchecked(exception);
});
r.onError(e -> {
received.set(e == exception);
onError.set(true);
});
r.onClose(() -> {
closed.set(true);
});
try {
r.start();
} catch (Exception e) {
throw new Exception("Unexpected exception thrown from start()", e);
}
if (!received.get()) {
throw new Exception("Did not receive expected exception in onError(...)");
}
if (exception.isPrinted()) {
throw new Exception("Expected stack trace from Exception NOT to be printed");
}
if (!onError.get()) {
throw new Exception("Expected OnError(...) to be invoked");
}
if (!closed.get()) {
throw new Exception("Expected stream to be closed");
}
} finally {
t.cancel();
}
}
private static void testOnFlushSanity() throws Exception {
TestException exception = new TestException();
AtomicBoolean received = new AtomicBoolean();
try (RecordingStream r = new RecordingStream()) {
r.onFlush(() -> {
TestUtils.throwUnchecked(exception);
});
r.onError(t -> {
received.set(t == exception);
r.close();
});
r.start();
if (!received.get()) {
throw new Exception("Expected exception in OnFlush to propagate to onError");
}
}
}
private static void testOnCloseSanity() throws Exception {
TestException exception = new TestException();
AtomicBoolean received = new AtomicBoolean();
try (RecordingStream r = new RecordingStream()) {
r.onFlush(() -> {
r.close(); // will trigger onClose
});
r.onClose(() -> {
TestUtils.throwUnchecked(exception); // will trigger onError
});
r.onError(t -> {
received.set(t == exception);
});
r.start();
if (!received.get()) {
throw new Exception("Expected exception in OnFlush to propagate to onError");
}
}
}
private static Timer newEventEmitter() {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
TestEvent event = new TestEvent();
event.commit();
}
};
timer.schedule(task, 0, 100);
return timer;
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.Name;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::onEvent(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnEvent
*/
public class TestOnEvent {
@Name("A")
static class EventA extends Event {
}
@Name("A")
static class EventAlsoA extends Event {
}
@Name("C")
static class EventC extends Event {
}
public static void main(String... args) throws Exception {
testOnEventNull();
testOnEvent();
testNamedEvent();
testTwoEventWithSameName();
}
private static void testOnEventNull() {
log("Entering testOnEventNull()");
try (RecordingStream rs = new RecordingStream()) {
try {
rs.onEvent(null);
throw new AssertionError("Expected NullPointerException from onEvent(null)");
} catch (NullPointerException npe) {
// OK; as expected
}
try {
rs.onEvent("A", null);
throw new AssertionError("Expected NullPointerException from onEvent(\"A\", null)");
} catch (NullPointerException npe) {
// OK; as expected
}
try {
String s = null;
rs.onEvent(s, null);
throw new AssertionError("Expected NullPointerException from onEvent(null, null)");
} catch (NullPointerException npe) {
// OK; as expected
}
}
log("Leaving testOnEventNull()");
}
private static void testTwoEventWithSameName() throws Exception {
log("Entering testTwoEventWithSameName()");
CountDownLatch eventA = new CountDownLatch(2);
try (RecordingStream r = new RecordingStream()) {
r.onEvent("A", e -> {
System.out.println("testTwoEventWithSameName" + e);
eventA.countDown();
});
r.startAsync();
EventA a1 = new EventA();
a1.commit();
EventAlsoA a2 = new EventAlsoA();
a2.commit();
eventA.await();
}
log("Leaving testTwoEventWithSameName()");
}
private static void testNamedEvent() throws Exception {
log("Entering testNamedEvent()");
try (RecordingStream r = new RecordingStream()) {
CountDownLatch eventA = new CountDownLatch(1);
CountDownLatch eventC = new CountDownLatch(1);
r.onEvent("A", e -> {
System.out.println("TestNamedEvent:" + e);
if (e.getEventType().getName().equals("A")) {
eventA.countDown();
}
});
r.onEvent("C", e -> {
System.out.println("TestNamedEvent:" + e);
if (e.getEventType().getName().equals("C")) {
eventC.countDown();
}
});
r.startAsync();
EventA a = new EventA();
a.commit();
EventC c = new EventC();
c.commit();
eventA.await();
eventC.await();
}
log("Leaving testNamedEvent()");
}
private static void testOnEvent() throws Exception {
log("Entering testOnEvent()");
try (RecordingStream r = new RecordingStream()) {
CountDownLatch event = new CountDownLatch(1);
r.onEvent(e -> {
event.countDown();
});
r.startAsync();
EventA a = new EventA();
a.commit();
event.await();
}
log("Leaving testOnEvent()");
}
private static void log(String msg) {
System.out.println(msg);
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::onFlush(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnFlush
*/
public class TestOnFlush {
static class OneEvent extends Event {
}
public static void main(String... args) throws Exception {
testOnFlushNull();
testOneEvent();
testNoEvent();
}
private static void testOnFlushNull() {
log("Entering testOnFlushNull()");
try (RecordingStream rs = new RecordingStream()) {
try {
rs.onFlush(null);
throw new AssertionError("Expected NullPointerException from onFlush(null");
} catch (NullPointerException npe) {
// OK; as expected
}
}
log("Leaving testOnFlushNull()");
}
private static void testNoEvent() throws Exception {
log("Entering testNoEvent()");
CountDownLatch flush = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onFlush(() -> {
flush.countDown();
});
r.startAsync();
flush.await();
}
log("Leaving testNoEvent()");
}
private static void testOneEvent() throws InterruptedException {
log("Entering testOneEvent()");
CountDownLatch flush = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
// ignore event
});
r.onFlush(() -> {
flush.countDown();
});
r.startAsync();
OneEvent e = new OneEvent();
e.commit();
flush.await();
}
log("Leaving testOneEvent()");
}
private static void log(String msg) {
System.out.println(msg);
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.Events;
/**
* @test
* @summary Tests that events are not emitted in handlers
* @key jfr
* @library /lib /
* @build jdk.jfr.api.consumer.recordingstream.EventProducer
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestRecursive
*/
public class TestRecursive {
public static class NotRecorded extends Event {
}
public static class Recorded extends Event {
}
public static class Provoker extends Event {
}
public static void main(String... args) throws Exception {
testSync();
testAsync();
testStreamInStream();
}
private static void testStreamInStream() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
try (Recording r = new Recording()) {
r.start();
Recorded r1 = new Recorded(); // 1
r1.commit();
try (RecordingStream rs = new RecordingStream()) {
rs.onEvent(e1 -> {
streamInStream();
latch.countDown();
});
rs.startAsync();
Recorded r2 = new Recorded(); // 2
r2.commit();
latch.await();
}
Recorded r3 = new Recorded(); // 2
r3.commit();
r.stop();
List<RecordedEvent> events = Events.fromRecording(r);
if (count(events, NotRecorded.class) != 0) {
throw new Exception("Expected 0 NotRecorded events");
}
if (count(events, Recorded.class) != 3) {
throw new Exception("Expected 3 Recorded events");
}
}
}
// No events should be recorded in this method
private static void streamInStream() {
NotRecorded nr1 = new NotRecorded();
nr1.commit();
CountDownLatch latch = new CountDownLatch(1);
try (RecordingStream rs2 = new RecordingStream()) {
rs2.onEvent(e2 -> {
NotRecorded nr2 = new NotRecorded();
nr2.commit();
latch.countDown();
});
NotRecorded nr3 = new NotRecorded();
nr3.commit();
rs2.startAsync();
// run event in separate thread
CompletableFuture.runAsync(() -> {
Provoker p = new Provoker();
p.commit();
});
try {
latch.await();
} catch (InterruptedException e) {
throw new Error("Unexpected interruption", e);
}
}
NotRecorded nr2 = new NotRecorded();
nr2.commit();
}
private static void testSync() throws Exception {
try (Recording r = new Recording()) {
r.start();
EventProducer p = new EventProducer();
try (RecordingStream rs = new RecordingStream()) {
Recorded e1 = new Recorded();
e1.commit();
rs.onEvent(e -> {
System.out.println("Emitting NotRecorded event");
NotRecorded event = new NotRecorded();
event.commit();
System.out.println("Stopping event provoker");
p.kill();
System.out.println("Closing recording stream");
rs.close();
return;
});
p.start();
rs.start();
Recorded e2 = new Recorded();
e2.commit();
}
r.stop();
List<RecordedEvent> events = Events.fromRecording(r);
System.out.println(events);
if (count(events, NotRecorded.class) != 0) {
throw new Exception("Expected 0 NotRecorded events");
}
if (count(events, Recorded.class) != 2) {
throw new Exception("Expected 2 Recorded events");
}
}
}
private static int count(List<RecordedEvent> events, Class<?> eventClass) {
int count = 0;
for (RecordedEvent e : events) {
if (e.getEventType().getName().equals(eventClass.getName())) {
count++;
}
}
System.out.println(count);
return count;
}
private static void testAsync() throws InterruptedException, Exception {
CountDownLatch latchOne = new CountDownLatch(1);
CountDownLatch latchTwo = new CountDownLatch(2);
AtomicBoolean fail = new AtomicBoolean();
try (RecordingStream r = new RecordingStream()) {
r.onEvent(e -> {
System.out.println(e);
NotRecorded event = new NotRecorded();
event.commit();
if (e.getEventType().getName().equals(Recorded.class.getName())) {
latchOne.countDown();
latchTwo.countDown();
}
if (e.getEventType().getName().equals(NotRecorded.class.getName())) {
fail.set(true);
}
});
r.startAsync();
Recorded e1 = new Recorded();
e1.commit();
latchOne.await();
Recorded e2 = new Recorded();
e2.commit();
latchTwo.await();
if (fail.get()) {
throw new Exception("Unexpected event found");
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStrream::remove(...)
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestRemove
*/
public class TestRemove {
static class RemoveEvent extends Event {
}
public static void main(String... args) throws Exception {
testRemoveNull();
testRemoveOnFlush();
testRemoveOnClose();
testRemoveOnEvent();
}
private static void testRemoveNull() {
log("Entering testRemoveNull()");
try (RecordingStream rs = new RecordingStream()) {
try {
rs.remove(null);
throw new AssertionError("Expected NullPointerException from remove(null");
} catch (NullPointerException npe) {
// OK; as expected
}
}
log("Leaving testRemoveNull()");
}
private static void testRemoveOnEvent() throws Exception {
log("Entering testRemoveOnEvent()");
try (RecordingStream rs = new RecordingStream()) {
AtomicInteger counter = new AtomicInteger(0);
CountDownLatch events = new CountDownLatch(2);
Consumer<RecordedEvent> c1 = e -> {
counter.incrementAndGet();
};
Consumer<RecordedEvent> c2 = e -> {
events.countDown();
};
rs.onEvent(c1);
rs.onEvent(c2);
rs.remove(c1);
rs.startAsync();
RemoveEvent r1 = new RemoveEvent();
r1.commit();
RemoveEvent r2 = new RemoveEvent();
r2.commit();
events.await();
if (counter.get() > 0) {
throw new AssertionError("OnEvent handler not removed!");
}
}
log("Leaving testRemoveOnEvent()");
}
private static void testRemoveOnClose() {
log("Entering testRemoveOnClose()");
try (RecordingStream rs = new RecordingStream()) {
AtomicBoolean onClose = new AtomicBoolean(false);
Runnable r = () -> {
onClose.set(true);
};
rs.onClose(r);
rs.remove(r);
rs.close();
if (onClose.get()) {
throw new AssertionError("onClose handler not removed!");
}
}
log("Leaving testRemoveOnClose()");
}
private static void testRemoveOnFlush() throws Exception {
log("Entering testRemoveOnFlush()");
try (RecordingStream rs = new RecordingStream()) {
AtomicInteger flushCount = new AtomicInteger(2);
AtomicBoolean removeExecuted = new AtomicBoolean(false);
Runnable onFlush1 = () -> {
removeExecuted.set(true);
};
Runnable onFlush2 = () -> {
flushCount.incrementAndGet();
};
rs.onFlush(onFlush1);
rs.onFlush(onFlush2);
rs.remove(onFlush1);
rs.startAsync();
while (flushCount.get() < 2) {
RemoveEvent r = new RemoveEvent();
r.commit();
Thread.sleep(100);
}
if (removeExecuted.get()) {
throw new AssertionError("onFlush handler not removed!");
}
}
log("Leaving testRemoveOnFlush()");
}
private static void log(String msg) {
System.out.println(msg);
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.jfr.Event;
import jdk.jfr.Name;
import jdk.jfr.Recording;
import jdk.jfr.StackTrace;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests EventStream::setEndTime
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetEndTime
*/
public final class TestSetEndTime {
@Name("Mark")
@StackTrace(false)
public final static class Mark extends Event {
public boolean include;
public int id;
}
public static void main(String... args) throws Exception {
testEventStream();
testRecordingStream();
}
private static void testRecordingStream() throws Exception {
while (true) {
CountDownLatch closed = new CountDownLatch(1);
AtomicInteger count = new AtomicInteger();
try (RecordingStream rs = new RecordingStream()) {
rs.setFlushInterval(Duration.ofSeconds(1));
rs.onEvent(e -> {
count.incrementAndGet();
});
// when end is reached stream is closed
rs.onClose(() -> {
closed.countDown();
});
Instant endTime = Instant.now().plus(Duration.ofMillis(100));
System.out.println("Setting end time: " + endTime);
rs.setEndTime(endTime);
rs.startAsync();
for (int i = 0; i < 50; i++) {
Mark m = new Mark();
m.commit();
Thread.sleep(10);
}
closed.await();
System.out.println("Found events: " + count.get());
if (count.get() < 50) {
return;
}
System.out.println("Found 50 events. Retrying");
System.out.println();
}
}
}
static void testEventStream() throws InterruptedException, IOException, Exception {
while (true) {
try (Recording r = new Recording()) {
r.start();
Mark event1 = new Mark();
event1.id = 1;
event1.include = false;
event1.commit(); // start time
nap();
Mark event2 = new Mark();
event2.id = 2;
event2.include = true;
event2.commit();
nap();
Mark event3 = new Mark();
event3.id = 3;
event3.include = false;
event3.commit(); // end time
Path p = Paths.get("recording.jfr");
r.dump(p);
Instant start = null;
Instant end = null;
System.out.println("Find start and end time as instants:");
for (RecordedEvent e : RecordingFile.readAllEvents(p)) {
if (e.getInt("id") == 1) {
start = e.getEndTime();
System.out.println("Start : " + start);
}
if (e.getInt("id") == 2) {
Instant middle = e.getEndTime();
System.out.println("Middle : " + middle);
}
if (e.getInt("id") == 3) {
end = e.getEndTime();
System.out.println("End : " + end);
}
}
System.out.println();
System.out.println("Opening stream between " + start + " and " + end);
AtomicBoolean success = new AtomicBoolean(false);
AtomicInteger eventsCount = new AtomicInteger();
try (EventStream d = EventStream.openRepository()) {
d.setStartTime(start.plusNanos(1));
// Stream should close when end is reached
d.setEndTime(end.minusNanos(1));
d.onEvent(e -> {
eventsCount.incrementAndGet();
boolean include = e.getBoolean("include");
System.out.println("Event " + e.getEndTime() + " include=" + include);
if (include) {
success.set(true);
}
});
d.start();
if (eventsCount.get() == 1 && success.get()) {
return;
}
}
}
}
}
private static void nap() throws InterruptedException {
// Ensure we advance at least 1 ns with fast time
Thread.sleep(1);
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.time.Duration;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Tests RecordingStream::setFlushInterval
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetFlushInterval
*/
public class TestSetFlushInterval {
public static void main(String... args) throws Exception {
Duration expectedDuration = Duration.ofMillis(1001);
try (RecordingStream r = new RecordingStream()) {
r.setFlushInterval(expectedDuration);
r.enable(EventNames.ActiveRecording);
r.onEvent(e -> {
System.out.println(e);
Duration duration = e.getDuration("flushInterval");
if (expectedDuration.equals(duration)) {
System.out.println("Closing recording");
r.close();
return;
}
System.out.println("Flush interval not set, was " + duration +
", but expected " + expectedDuration);
});
r.start();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.time.Duration;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Tests RecordingStrream::setMaxAge
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxAge
*/
public class TestSetMaxAge {
public static void main(String... args) throws Exception {
Duration expecteddAge = Duration.ofMillis(123456789);
try (RecordingStream r = new RecordingStream()) {
r.setMaxAge(expecteddAge);
r.enable(EventNames.ActiveRecording);
r.onEvent(e -> {
System.out.println(e);
Duration age = e.getDuration("maxAge");
if (expecteddAge.equals(age)) {
r.close();
return;
}
System.out.println("Max age not set, was " + age + ", but expected " + expecteddAge);
});
r.start();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Tests RecordingStrream::setMaxSize
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxSize
*/
public class TestSetMaxSize {
public static void main(String... args) throws Exception {
long testSize = 123456789;
try (RecordingStream r = new RecordingStream()) {
r.setMaxSize(123456789);
r.enable(EventNames.ActiveRecording);
r.onEvent(e -> {
System.out.println(e);
long size= e.getLong("maxSize");
if (size == testSize) {
r.close();
return;
}
System.out.println("Max size not set, was " + size + ", but expected " + testSize);
});
r.start();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Enabled;
import jdk.jfr.Event;
import jdk.jfr.Name;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::setSettings
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetSettings
*/
public final class TestSetSettings {
@Name("LateBloomer")
@Enabled(false)
private final static class LateBloomer extends Event {
}
private static CountDownLatch lateBloomer = new CountDownLatch(1);
public static void main(String... args) throws Exception {
try (RecordingStream r = new RecordingStream()) {
r.startAsync();
Map<String, String> settings = new HashMap<String, String>();
settings.put("LateBloomer#enabled", "true");
r.setSettings(settings);
r.onEvent("LateBloomer", e -> {
lateBloomer.countDown();
});
LateBloomer event = new LateBloomer();
event.commit();
lateBloomer.await();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.Event;
import jdk.jfr.Name;
import jdk.jfr.Recording;
import jdk.jfr.StackTrace;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests EventStream::setStartTime
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetStartTime
*/
public final class TestSetStartTime {
private final static int SLEEP_TIME_MS = 100;
@Name("Mark")
@StackTrace(false)
public final static class Mark extends Event {
public boolean before;
}
public static void main(String... args) throws Exception {
testEventStream();
testRecordingStream();
}
private static void testRecordingStream() throws InterruptedException {
AtomicBoolean exit = new AtomicBoolean();
int attempt = 1;
while (!exit.get()) {
System.out.println("Testing RecordingStream:setStartTime(...). Attempt: " + attempt++);
AtomicBoolean firstEvent = new AtomicBoolean(true);
try (RecordingStream r2 = new RecordingStream()) {
Instant t = Instant.now().plus(Duration.ofMillis(SLEEP_TIME_MS / 2));
System.out.println("Setting start time: " + t);
r2.setStartTime(t);
r2.onEvent(e -> {
if (firstEvent.get()) {
firstEvent.set(false);
if (!e.getBoolean("before")) {
// Skipped first event, let's exit
exit.set(true);
}
r2.close();
}
});
r2.startAsync();
Mark m1 = new Mark();
m1.before = true;
m1.commit();
System.out.println("First event emitted: " + Instant.now());
Thread.sleep(SLEEP_TIME_MS);
Mark m2 = new Mark();
m2.before = false;
m2.commit();
System.out.println("Second event emitted: " + Instant.now());
r2.awaitTermination();
}
System.out.println();
}
}
private static void testEventStream() throws InterruptedException, Exception, IOException {
AtomicBoolean exit = new AtomicBoolean();
int attempt = 1;
while (!exit.get()) {
System.out.println("Testing EventStream:setStartTime(...). Attempt: " + attempt++);
AtomicBoolean firstEvent = new AtomicBoolean(true);
try (Recording r = new Recording()) {
r.start();
Mark event1 = new Mark();
event1.before = true;
event1.commit();
Instant t = Instant.now();
System.out.println("First event emitted: " + t);
Thread.sleep(SLEEP_TIME_MS);
Mark event2 = new Mark();
event2.before = false;
event2.commit();
System.out.println("Second event emitted: " + Instant.now());
try (EventStream es = EventStream.openRepository()) {
Instant startTime = t.plus(Duration.ofMillis(SLEEP_TIME_MS / 2));
es.setStartTime(startTime);
System.out.println("Setting start time: " + startTime);
es.onEvent(e -> {
if (firstEvent.get()) {
firstEvent.set(false);
if (!e.getBoolean("before")) {
// Skipped first event, let's exit
exit.set(true);
}
es.close();
}
});
es.startAsync();
es.awaitTermination();
}
}
System.out.println();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::start()
* @key jfr
* @library /lib /
* @build jdk.jfr.api.consumer.recordingstream.EventProducer
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStart
*/
public class TestStart {
static class StartEvent extends Event {
}
public static void main(String... args) throws Exception {
testStart();
testStartOnEvent();
testStartTwice();
testStartClosed();
}
private static void testStartTwice() throws Exception {
log("Entering testStartTwice()");
CountDownLatch started = new CountDownLatch(1);
try (RecordingStream rs = new RecordingStream()) {
EventProducer t = new EventProducer();
t.start();
Thread thread = new Thread() {
public void run() {
rs.start();
}
};
thread.start();
rs.onEvent(e -> {
if (started.getCount() > 0) {
started.countDown();
}
});
started.await();
t.kill();
try {
rs.start();
throw new AssertionError("Expected IllegalStateException if started twice");
} catch (IllegalStateException ise) {
// OK, as expected
}
}
log("Leaving testStartTwice()");
}
static void testStart() throws Exception {
log("Entering testStart()");
CountDownLatch started = new CountDownLatch(1);
try (RecordingStream rs = new RecordingStream()) {
rs.onEvent(e -> {
started.countDown();
});
EventProducer t = new EventProducer();
t.start();
Thread thread = new Thread() {
public void run() {
rs.start();
}
};
thread.start();
started.await();
t.kill();
}
log("Leaving testStart()");
}
static void testStartOnEvent() throws Exception {
log("Entering testStartOnEvent()");
AtomicBoolean ISE = new AtomicBoolean(false);
CountDownLatch startedTwice = new CountDownLatch(1);
try (RecordingStream rs = new RecordingStream()) {
rs.onEvent(e -> {
try {
rs.start(); // must not deadlock
} catch (IllegalStateException ise) {
if (!ISE.get()) {
ISE.set(true);
startedTwice.countDown();
}
}
});
EventProducer t = new EventProducer();
t.start();
Thread thread = new Thread() {
public void run() {
rs.start();
}
};
thread.start();
startedTwice.await();
t.kill();
if (!ISE.get()) {
throw new AssertionError("Expected IllegalStateException");
}
}
log("Leaving testStartOnEvent()");
}
static void testStartClosed() {
log("Entering testStartClosed()");
RecordingStream rs = new RecordingStream();
rs.close();
try {
rs.start();
throw new AssertionError("Expected IllegalStateException");
} catch (IllegalStateException ise) {
// OK, as expected.
}
log("Leaving testStartClosed()");
}
private static void log(String msg) {
System.out.println(msg);
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests RecordingStream::startAsync()
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStartAsync
*/
public class TestStartAsync {
static class StartEvent extends Event {
}
public static void main(String... args) throws Exception {
testStart();
testStartTwice();
testStartClosed();
}
private static void testStartTwice() throws Exception {
try (RecordingStream rs = new RecordingStream()) {
rs.startAsync();
try {
rs.startAsync();
throw new AssertionError("Expected IllegalStateException if started twice");
} catch (IllegalStateException ise) {
// OK, as expected
}
}
}
static void testStart() throws Exception {
CountDownLatch started = new CountDownLatch(1);
try (RecordingStream rs = new RecordingStream()) {
rs.onEvent(e -> {
started.countDown();
});
rs.startAsync();
StartEvent e = new StartEvent();
e.commit();
started.await();
}
}
static void testStartClosed() {
RecordingStream rs = new RecordingStream();
rs.close();
try {
rs.startAsync();
throw new AssertionError("Expected IllegalStateException");
} catch (IllegalStateException ise) {
// OK, as expected.
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.recordingstream;
import java.util.concurrent.CountDownLatch;
public class TestUtils {
public static final class TestError extends Error {
private static final long serialVersionUID = 1L;
}
public static final class TestException extends Exception {
private static final long serialVersionUID = 1L;
private volatile boolean printed;
@Override
public void printStackTrace() {
super.printStackTrace();
printed = true;
}
public boolean isPrinted() {
return printed;
}
}
// Can throw checked exception as unchecked.
@SuppressWarnings("unchecked")
public static <T extends Throwable> void throwUnchecked(Throwable e) throws T {
throw (T) e;
}
public static void installUncaughtException(CountDownLatch receivedError, Throwable expected) {
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
if (throwable == expected) {
System.out.println("Received uncaught exception " + expected.getClass());
receivedError.countDown();
}
});
}
}
/* /*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -22,26 +22,28 @@ ...@@ -22,26 +22,28 @@
* or visit www.oracle.com if you need additional information or have any * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package jdk.jfr.internal.consumer;
import java.io.IOException; package jdk.jfr.api.consumer.security;
import java.util.List;
import jdk.jfr.consumer.RecordedEvent; import java.nio.file.Paths;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.Type;
public abstract class RecordingInternals { import jdk.jfr.Recording;
import jdk.test.lib.jfr.EventNames;
public static RecordingInternals INSTANCE;
public abstract boolean isLastEventInChunk(RecordingFile file);
public abstract Object getOffsetDataTime(RecordedObject event, String name);
public abstract List<Type> readTypes(RecordingFile file) throws IOException;
public abstract void sort(List<RecordedEvent> events);
/**
* Driver that dumps a recording with a JVMInformation event
*
* Usage:
*
* @run driver jdk.jfr.api.consumer.security.RecordingDumper <filename>
*/
public class DriverRecordingDumper {
public static void main(String... args) throws Exception {
try (Recording r = new Recording()) {
r.enable(EventNames.JVMInformation);
r.start();
r.stop();
r.dump(Paths.get(args[0]));
}
}
} }
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.security;
import java.io.IOException;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests that streaming doesn't work if
* FlightRecordingPermission("accessFlightRecorder") is missing
* @key jfr
* @library /lib /
*
* @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
* jdk.jfr.api.consumer.security.TestMissingPermission
*/
public class TestMissingPermission {
public static void main(String... args) throws Exception {
testOpenRepository();
testRecordingStream();
}
private static void testRecordingStream() throws IOException {
try {
try (EventStream es = EventStream.openRepository()) {
throw new AssertionError("Should not be able to create EventStream without FlightRecorderPermission");
}
} catch (SecurityException se) {
// OK, as expected
}
}
private static void testOpenRepository() throws IOException {
try {
try (RecordingStream es = new RecordingStream()) {
throw new AssertionError("Should not be able to create RecordingStream without FlightRecorderPermission");
}
} catch (SecurityException se) {
// OK, as expected
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.security;
import java.nio.file.Paths;
import jdk.jfr.consumer.RecordingFile;
/**
* @test
* @summary Test that a recording file can't be opened without permissions
* @key jfr
* @library /lib /
*
* @run driver jdk.jfr.api.consumer.security.DriverRecordingDumper
* test-recording-file.jfr
* @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
* jdk.jfr.api.consumer.security.TestRecordingFile
* test-recording-file.jfr
*/
public class TestRecordingFile {
public static void main(String... args) throws Exception {
try {
RecordingFile.readAllEvents(Paths.get(args[0]));
throw new AssertionError("Expected SecurityException");
} catch (SecurityException se) {
// OK, as expected
return;
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.security;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.consumer.RecordingStream;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Tests that a RecordingStream works using only
* FlightRecordingPermission("accessFlightRecorder")
* @key jfr
* @library /lib /
*
* @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy
* jdk.jfr.api.consumer.security.TestStreamingLocal
*/
public class TestRecordingStream {
public static void main(String... args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
// Enable JVM event, no write permission needed
r.enable(EventNames.JVMInformation);
r.setStartTime(Instant.EPOCH);
r.onEvent(EventNames.JVMInformation, e -> {
latch.countDown();
});
r.startAsync();
latch.await();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.security;
import java.nio.file.Paths;
import jdk.jfr.consumer.EventStream;
/**
* @test
* @summary Test that an event file stream can't be opened without permissions
* @key jfr
* @library /lib /
*
* @run driver jdk.jfr.api.consumer.security.DriverRecordingDumper
* test-streaming-file.jfr
* @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
* jdk.jfr.api.consumer.security.TestStreamingFile
* test-streaming-file.jfr
*/
public class TestStreamingFile {
public static void main(String... args) throws Exception {
try (EventStream es = EventStream.openFile(Paths.get(args[0]))) {
throw new AssertionError("Expected SecurityException");
} catch (SecurityException se) {
// OK, as expected
return;
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.security;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Tests that local streaming works using only
* FlightRecordingPermission("accessFlightRecorder")
* @key jfr
* @library /lib /
*
* @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy
* jdk.jfr.api.consumer.security.TestStreamingLocal
*/
public class TestStreamingLocal {
public static void main(String... args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
try (Recording r = new Recording()) {
// Enable JVM event, no write permission needed
r.enable(EventNames.JVMInformation);
r.start();
try (Recording r2 = new Recording()){
r2.start();
r2.stop();
}
r.stop();
try (EventStream es = EventStream.openRepository()) {
es.setStartTime(Instant.EPOCH);
es.onEvent("jdk.JVMInformation", e -> {
latch.countDown();
});
es.startAsync();
latch.await();
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.security;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
/**
* @test
* @summary Test that a stream can be opened against a remote repository using
* only file permission
* @key jfr
* @library /lib /
*
* @run main/othervm jdk.jfr.api.consumer.security.TestStreamingRemote
*/
public class TestStreamingRemote {
private static final String SUCCESS = "Success!";
public static class TestEvent extends Event {
}
public static class Test {
public static void main(String... args) throws Exception {
Path repo = Paths.get(args[0]);
System.out.println("Repository: " + repo);
try (EventStream es = EventStream.openRepository(repo)) {
es.setStartTime(Instant.EPOCH);
es.onEvent(e -> {
System.out.println(SUCCESS);
es.close();
});
es.start();
}
}
}
public static void main(String... args) throws Exception {
try (Recording r = new Recording()) {
r.setFlushInterval(Duration.ofSeconds(1));
r.start();
String repository = System.getProperty("jdk.jfr.repository");
Path policy = createPolicyFile(repository);
TestEvent e = new TestEvent();
e.commit();
String[] c = new String[4];
c[0] = "-Djava.security.manager";
c[1] = "-Djava.security.policy=" + escapeBackslashes(policy.toString());
c[2] = Test.class.getName();
c[3] = repository;
OutputAnalyzer oa = ProcessTools.executeTestJvm(c);
oa.shouldContain(SUCCESS);
}
}
private static Path createPolicyFile(String repository) throws IOException {
Path policy = Paths.get("permission.policy").toAbsolutePath();
try (PrintWriter pw = new PrintWriter(policy.toFile())) {
pw.println("grant {");
// All the files and directories the contained in path
String dir = escapeBackslashes(repository);
String contents = escapeBackslashes(repository + File.separatorChar + "-");
pw.println(" permission java.io.FilePermission \"" + dir + "\", \"read\";");
pw.println(" permission java.io.FilePermission \"" + contents + "\", \"read\";");
pw.println("};");
pw.println();
}
System.out.println("Permission file: " + policy);
for (String line : Files.readAllLines(policy)) {
System.out.println(line);
}
System.out.println();
return policy;
}
// Needed for Windows
private static String escapeBackslashes(String text) {
return text.replace("\\", "\\\\");
}
}
// Minimum policy to stream locally
grant {
permission jdk.jfr.FlightRecorderPermission "accessFlightRecorder";
};
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
/**
* @test
* @summary Tests that a stream can gracefully handle chunk being removed in the
* middle
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
*/
public class TestChunkGap {
static class StartEvent extends Event {
}
static class TestGapEvent extends Event {
}
static class EndEvent extends Event {
}
private final static AtomicInteger count = new AtomicInteger(0);
public static void main(String... args) throws Exception {
CountDownLatch gap = new CountDownLatch(1);
CountDownLatch receivedEvent = new CountDownLatch(1);
try (EventStream s = EventStream.openRepository()) {
try (Recording r1 = new Recording()) {
s.setStartTime(Instant.EPOCH);
s.onEvent(e -> {
System.out.println(e);
receivedEvent.countDown();
try {
gap.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
count.incrementAndGet();
if (e.getEventType().getName().equals(EndEvent.class.getName())) {
s.close();
}
});
s.startAsync();
r1.start();
StartEvent event1 = new StartEvent();
event1.commit();
receivedEvent.await();
r1.stop();
// create chunk that is removed
try (Recording r2 = new Recording()) {
r2.enable(TestGapEvent.class);
r2.start();
TestGapEvent event2 = new TestGapEvent();
event2.commit();
r2.stop();
}
gap.countDown();
try (Recording r3 = new Recording()) {
r3.enable(EndEvent.class);
r3.start();
EndEvent event3 = new EndEvent();
event3.commit();
r3.stop();
s.awaitTermination();
if (count.get() != 2) {
throw new AssertionError("Expected 2 event, but got " + count);
}
}
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Test that it is possible to iterate over chunk without normal events
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestEmptyChunks
*/
public class TestEmptyChunks {
static class EndEvent extends Event {
}
public static void main(String... args) throws Exception {
CountDownLatch end = new CountDownLatch(1);
CountDownLatch firstFlush = new CountDownLatch(1);
try (RecordingStream es = new RecordingStream()) {
es.onEvent(EndEvent.class.getName(), e -> {
end.countDown();
});
es.onFlush(() -> {
firstFlush.countDown();
});
es.startAsync();
System.out.println("Invoked startAsync()");
// Wait for stream thread to start
firstFlush.await();
System.out.println("Stream thread active");
Recording r1 = new Recording();
r1.start();
System.out.println("Chunk 1 started");
Recording r2 = new Recording();
r2.start();
System.out.println("Chunk 2 started");
Recording r3 = new Recording();
r3.start();
System.out.println("Chunk 3 started");
r2.stop();
System.out.println("Chunk 4 started");
r3.stop();
System.out.println("Chunk 5 started");
EndEvent e = new EndEvent();
e.commit();
end.await();
r1.stop();
System.out.println("Chunk 5 ended");
r1.close();
r2.close();
r3.close();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Enabled;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Verifies that it is possible to stream contents from specified event
* settings
* @key jfr
* @library /lib /
*
* @run main/othervm jdk.jfr.api.consumer.streaming.TestEnableEvents
*/
public class TestEnableEvents {
@Enabled(false)
static class HorseEvent extends Event {
}
@Enabled(false)
static class ElephantEvent extends Event {
}
@Enabled(false)
static class TigerEvent extends Event {
}
public static void main(String... args) throws Exception {
CountDownLatch elephantLatch = new CountDownLatch(1);
CountDownLatch tigerLatch = new CountDownLatch(1);
CountDownLatch horseLatch = new CountDownLatch(1);
FlightRecorder.addPeriodicEvent(ElephantEvent.class, () -> {
HorseEvent ze = new HorseEvent();
ze.commit();
});
try (RecordingStream s = new RecordingStream()) {
s.enable(HorseEvent.class.getName()).withPeriod(Duration.ofMillis(50));
s.enable(TigerEvent.class.getName());
s.enable(ElephantEvent.class.getName());
s.onEvent(TigerEvent.class.getName(), e -> {
System.out.println("Event: " + e.getEventType().getName());
System.out.println("Found tiger!");
tigerLatch.countDown();
});
s.onEvent(HorseEvent.class.getName(), e -> {
System.out.println("Found horse!");
horseLatch.countDown();
});
s.onEvent(ElephantEvent.class.getName(), e -> {
System.out.println("Found elelphant!");
elephantLatch.countDown();
});
s.startAsync();
TigerEvent te = new TigerEvent();
te.commit();
ElephantEvent ee = new ElephantEvent();
ee.commit();
elephantLatch.await();
horseLatch.await();
tigerLatch.await();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Registered;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Test that it is possible to register new metadata in a chunk
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestEventRegistration
*/
public class TestEventRegistration {
@Registered(false)
static class StreamEvent1 extends Event {
}
@Registered(false)
static class StreamEvent2 extends Event {
}
public static void main(String... args) throws Exception {
CountDownLatch s1Latch = new CountDownLatch(1);
CountDownLatch s2Latch = new CountDownLatch(1);
try (RecordingStream es = new RecordingStream()) {
es.onEvent(StreamEvent1.class.getName(), e -> {
s1Latch.countDown();
});
es.onEvent(StreamEvent2.class.getName(), e -> {
s2Latch.countDown();
});
es.startAsync();
System.out.println("Registering " + StreamEvent1.class.getName());
FlightRecorder.register(StreamEvent1.class);
StreamEvent1 s1 = new StreamEvent1();
s1.commit();
System.out.println(StreamEvent1.class.getName() + " commited");
System.out.println("Awaiting latch for " + StreamEvent1.class.getName());
s1Latch.await();
System.out.println();
System.out.println("Registering " + StreamEvent2.class.getName());
FlightRecorder.register(StreamEvent2.class);
StreamEvent2 s2 = new StreamEvent2();
s2.commit();
System.out.println(StreamEvent2.class.getName() + " commited");
System.out.println("Awaiting latch for " + StreamEvent2.class.getName());
s2Latch.await();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.Random;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Test that it is possible to iterate over chunk with normal events
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
*/
public class TestFilledChunks {
static class FillEvent extends Event {
String message;
int value;
int id;
}
static class EndEvent extends Event {
}
// Will generate about 100 MB of data, or 8-9 chunks
private static final int EVENT_COUNT = 5_000_000;
public static void main(String... args) throws Exception {
try (RecordingStream rs = new RecordingStream()) {
rs.onEvent(FillEvent.class.getName(), e -> {
int id = e.getInt("id");
// Some events may get lost due to
// buffer being full.
if (id > EVENT_COUNT / 2) {
rs.close();
}
});
rs.startAsync();
long seed = System.currentTimeMillis();
System.out.println("Random seed: " + seed);
Random r = new Random(seed);
for (int i = 1; i < EVENT_COUNT; i++) {
FillEvent f = new FillEvent();
f.message = i % 2 == 0 ? "hello, hello, hello, hello, hello!" : "hi!";
f.value = r.nextInt(10000);
f.id = i;
f.commit();
if (i % 1_000_000 == 0) {
System.out.println("Emitted " + i + " events");
}
}
rs.awaitTermination();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.jfr.Event;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Verifies that it is possible to filter a stream for an event
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestFiltering
*/
public class TestFiltering {
static class SnakeEvent extends Event {
int id;
}
static class EelEvent extends Event {
int id;
}
public static void main(String... args) throws Exception {
CountDownLatch l = new CountDownLatch(1);
String eventName = SnakeEvent.class.getName();
AtomicInteger idCounter = new AtomicInteger(-1);
try (RecordingStream e = new RecordingStream()) {
e.onEvent(eventName, event -> {
if (!event.getEventType().getName().equals(eventName)) {
throw new InternalError("Unexpected event " + e);
}
if (event.getInt("id") != idCounter.incrementAndGet()) {
throw new InternalError("Incorrect id");
}
if (idCounter.get() == 99) {
l.countDown();
}
});
e.startAsync();
for (int i = 0; i < 100; i++) {
SnakeEvent se = new SnakeEvent();
se.id = i;
se.commit();
EelEvent ee = new EelEvent();
ee.id = i;
ee.commit();
}
l.await();
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Name;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Verifies that EventStream::openRepository() read from the latest flush
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestLatestEvent
*/
public class TestLatestEvent {
@Name("NotLatest")
static class NotLatestEvent extends Event {
public int id;
}
@Name("Latest")
static class LatestEvent extends Event {
}
@Name("MakeChunks")
static class MakeChunks extends Event {
}
public static void main(String... args) throws Exception {
CountDownLatch notLatestEvent = new CountDownLatch(6);
CountDownLatch beginChunks = new CountDownLatch(1);
try (RecordingStream r = new RecordingStream()) {
r.onEvent("MakeChunks", event-> {
System.out.println(event);
beginChunks.countDown();
});
r.onEvent("NotLatest", event -> {
System.out.println(event);
notLatestEvent.countDown();
});
r.startAsync();
MakeChunks e = new MakeChunks();
e.commit();
System.out.println("Waiting for first chunk");
beginChunks.await();
// Create 5 chunks with events in the repository
for (int i = 0; i < 5; i++) {
System.out.println("Creating empty chunk");
try (Recording r1 = new Recording()) {
r1.start();
NotLatestEvent notLatest = new NotLatestEvent();
notLatest.id = i;
notLatest.commit();
r1.stop();
}
}
System.out.println("All empty chunks created");
// Create event in new chunk
NotLatestEvent notLatest = new NotLatestEvent();
notLatest.id = 5;
notLatest.commit();
// This latch ensures thatNotLatest has been
// flushed and a new valid position has been written
// to the chunk header
notLatestEvent.await(80, TimeUnit.SECONDS);
if (notLatestEvent.getCount() != 0) {
Recording rec = FlightRecorder.getFlightRecorder().takeSnapshot();
Path p = Paths.get("error-not-latest.jfr").toAbsolutePath();
rec.dump(p);
System.out.println("Dumping repository as a file for inspection at " + p);
throw new Exception("Timeout 80 s. Expected 6 event, but got " + notLatestEvent.getCount());
}
try (EventStream s = EventStream.openRepository()) {
System.out.println("EventStream opened");
AtomicBoolean foundLatest = new AtomicBoolean();
s.onEvent(event -> {
String name = event.getEventType().getName();
System.out.println("Found event " + name);
foundLatest.set(name.equals("Latest"));
});
s.startAsync();
// Must loop here as there is no guarantee
// that the parser thread starts before event
// is flushed
while (!foundLatest.get()) {
LatestEvent latest = new LatestEvent();
latest.commit();
System.out.println("Latest event emitted. Waiting 1 s ...");
Thread.sleep(1000);
}
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Verifies that it is possible to start a stream when there are
* already chunk in the repository
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestRecordingBefore
*/
public class TestRecordingBefore {
static class SnakeEvent extends Event {
int id;
}
public static void main(String... args) throws Exception {
try (Recording r1 = new Recording()) {
r1.start();
emitSnakeEvent(1);
emitSnakeEvent(2);
emitSnakeEvent(3);
// Force a chunk rotation
try (Recording r2 = new Recording()) {
r2.start();
emitSnakeEvent(4);
emitSnakeEvent(5);
emitSnakeEvent(6);
r2.stop();
}
r1.stop();
// Two chunks should now exist in the repository
AtomicBoolean fail = new AtomicBoolean(false);
CountDownLatch lastEvent = new CountDownLatch(1);
try (RecordingStream rs = new RecordingStream()) {
rs.onEvent(e -> {
long id = e.getLong("id");
if (id < 7) {
System.out.println("Found unexpected id " + id);
fail.set(true);
}
if (id == 9) {
lastEvent.countDown();
}
});
rs.startAsync();
emitSnakeEvent(7);
emitSnakeEvent(8);
emitSnakeEvent(9);
lastEvent.await();
if (fail.get()) {
throw new Exception("Found events from a previous recording");
}
}
}
}
static void emitSnakeEvent(int id) {
SnakeEvent e = new SnakeEvent();
e.id = id;
e.commit();
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Tests that a stream can gracefully handle chunk being removed
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestRemovedChunks
*/
public class TestRemovedChunks {
private final static CountDownLatch parkLatch = new CountDownLatch(1);
private final static CountDownLatch removalLatch = new CountDownLatch(1);
private final static CountDownLatch IFeelFineLatch = new CountDownLatch(1);
static class DataEvent extends Event {
double double1;
double double2;
double double3;
double double4;
double double5;
}
static class ParkStream extends Event {
}
static class IFeelFine extends Event {
}
public static void main(String... args) throws Exception {
try (RecordingStream s = new RecordingStream()) {
s.setMaxSize(5_000_000);
s.onEvent(ParkStream.class.getName(), e -> {
parkLatch.countDown();
await(removalLatch);
});
s.onEvent(IFeelFine.class.getName(), e -> {
IFeelFineLatch.countDown();
});
s.startAsync();
// Fill first chunk with data
emitData(1_000_000);
// Park stream
ParkStream ps = new ParkStream();
ps.commit();
await(parkLatch);
// Rotate and emit data that exceeds maxSize
for (int i = 0; i< 10;i++) {
try (Recording r = new Recording()) {
r.start();
emitData(1_000_000);
}
}
// Emit final event
IFeelFine i = new IFeelFine();
i.commit();
// Wake up parked stream
removalLatch.countDown();
// Await event things gone bad
await(IFeelFineLatch);
}
}
private static void await(CountDownLatch latch) throws Error {
try {
latch.await();
} catch (InterruptedException e1) {
throw new Error("Latch interupted");
}
}
private static void emitData(int amount) throws InterruptedException {
int count = 0;
while (amount > 0) {
DataEvent de = new DataEvent();
// 5 doubles are 40 bytes bytes
// and event size, event type, thread,
// start time, duration and stack trace about 15 bytes
de.double1 = 0.0;
de.double2 = 1.0;
de.double3 = 2.0;
de.double4 = 3.0;
de.double5 = 4.0;
de.commit();
amount -= 55;
count++;
//
if (count % 100_000 == 0) {
Thread.sleep(10);
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.jcmd.JcmdHelper;
/**
* @test
* @summary Verifies that is possible to stream from a repository that is being
* moved.
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestRepositoryMigration
*/
public class TestRepositoryMigration {
static class MigrationEvent extends Event {
int id;
}
public static void main(String... args) throws Exception {
Path newRepository = Paths.get("new-repository");
CountDownLatch event1 = new CountDownLatch(1);
CountDownLatch event2 = new CountDownLatch(1);
try (EventStream es = EventStream.openRepository()) {
es.setStartTime(Instant.EPOCH);
es.onEvent(e -> {
System.out.println(e);
if (e.getInt("id") == 1) {
event1.countDown();
}
if (e.getInt("id") == 2) {
event2.countDown();
}
});
es.startAsync();
System.out.println("Started es.startAsync()");
try (Recording r = new Recording()) {
r.setFlushInterval(Duration.ofSeconds(1));
r.start();
// Chunk in default repository
MigrationEvent e1 = new MigrationEvent();
e1.id = 1;
e1.commit();
event1.await();
System.out.println("Passed the event1.await()");
JcmdHelper.jcmd("JFR.configure", "repositorypath=" + newRepository.toAbsolutePath());
// Chunk in new repository
MigrationEvent e2 = new MigrationEvent();
e2.id = 2;
e2.commit();
r.stop();
event2.await();
System.out.println("Passed the event2.await()");
// Verify that it happened in new repository
if (!Files.exists(newRepository)) {
throw new AssertionError("Could not find repository " + newRepository);
}
System.out.println("Listing contents in new repository:");
boolean empty = true;
for (Path p : Files.newDirectoryStream(newRepository)) {
System.out.println(p.toAbsolutePath());
empty = false;
}
System.out.println();
if (empty) {
throw new AssertionError("Could not find contents in new repository location " + newRepository);
}
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Properties;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import jdk.jfr.Recording;
import jdk.test.lib.dcmd.CommandExecutor;
import jdk.test.lib.dcmd.PidJcmdExecutor;
import jdk.test.lib.process.ProcessTools;
/**
* @test
* @summary Verifies that it is possible to access JFR repository from a system
* property
* @key jfr
* @library /lib /
* @modules jdk.attach
* jdk.jfr
* @run main/othervm -Djdk.attach.allowAttachSelf=true jdk.jfr.api.consumer.streaming.TestRepositoryProperty
*/
public class TestRepositoryProperty {
private final static String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
public static void main(String... args) throws Exception {
testBeforeInitialization();
testAfterInitialization();
testFromAgent();
testAfterChange();
}
private static void testFromAgent() throws Exception {
String pidText = String.valueOf(ProcessTools.getProcessId());
VirtualMachine vm = VirtualMachine.attach(pidText);
Properties p = vm.getSystemProperties();
String location = (String) p.get(JFR_REPOSITORY_LOCATION_PROPERTY);
if (location == null) {
throw new AssertionError("Could not find repository path in agent properties");
}
Path path = FileSystems.getDefault().getPath(location);
if (!Files.isDirectory(path)) {
throw new AssertionError("Repository path doesn't point to directory");
}
}
private static void testAfterChange() {
Path newRepository = FileSystems.getDefault().getPath(".").toAbsolutePath();
String cmd = "JFR.configure repository=" + newRepository.toString();
CommandExecutor executor = new PidJcmdExecutor();
executor.execute(cmd);
String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
if (newRepository.toString().equals(location)) {
throw new AssertionError("Repository path not updated after it has been changed");
}
}
private static void testAfterInitialization() {
try (Recording r = new Recording()) {
r.start();
String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
if (location == null) {
throw new AssertionError("Repository path should exist after JFR is initialized");
}
System.out.println("repository=" + location);
Path p = FileSystems.getDefault().getPath(location);
if (!Files.isDirectory(p)) {
throw new AssertionError("Repository path doesn't point to directory");
}
}
}
private static void testBeforeInitialization() {
String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
if (location != null) {
throw new AssertionError("Repository path should not exist before JFR is initialized");
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Name;
import jdk.jfr.Period;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Verifies that it is possible to stream contents of ongoing
* recordings
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestStartMultiChunk
*/
public class TestStartMultiChunk {
@Period("10 s")
@Name("Zebra")
static class ZebraEvent extends Event {
}
@Name("Cat")
static class CatEvent extends Event {
}
@Name("Dog")
static class DogEvent extends Event {
}
@Name("Mouse")
static class MouseEvent extends Event {
}
public static void main(String... args) throws Exception {
CountDownLatch dogLatch = new CountDownLatch(1);
CountDownLatch catLatch = new CountDownLatch(1);
CountDownLatch mouseLatch = new CountDownLatch(1);
CountDownLatch zebraLatch = new CountDownLatch(3);
FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
ZebraEvent ze = new ZebraEvent();
ze.commit();
System.out.println("Zebra emitted");
});
try (RecordingStream s = new RecordingStream()) {
s.onEvent("Cat", e -> {
System.out.println("Found cat!");
catLatch.countDown();
});
s.onEvent("Dog", e -> {
System.out.println("Found dog!");
dogLatch.countDown();
});
s.onEvent("Zebra", e -> {
System.out.println("Found zebra!");
zebraLatch.countDown();
});
s.onEvent("Mouse", e -> {
System.out.println("Found mouse!");
mouseLatch.countDown();
});
s.startAsync();
System.out.println("Stream recoding started");
try (Recording r1 = new Recording()) {
r1.start();
System.out.println("r1.start()");
MouseEvent me = new MouseEvent();
me.commit();
System.out.println("Mouse emitted");
mouseLatch.await();
try (Recording r2 = new Recording()) { // force chunk rotation
// in stream
r2.start();
System.out.println("r2.start()");
DogEvent de = new DogEvent();
de.commit();
System.out.println("Dog emitted");
dogLatch.await();
CatEvent ce = new CatEvent();
ce.commit();
System.out.println("Cat emitted");
catLatch.await();
zebraLatch.await();
}
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.consumer.streaming;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Period;
import jdk.jfr.consumer.RecordingStream;
/**
* @test
* @summary Verifies that it is possible to stream contents of ongoing
* recordings
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestStartSingleChunk
*/
public class TestStartSingleChunk {
@Period("500 ms")
static class ElkEvent extends Event {
}
static class FrogEvent extends Event {
}
static class LionEvent extends Event {
}
public static void main(String... args) throws Exception {
CountDownLatch frogLatch = new CountDownLatch(1);
CountDownLatch lionLatch = new CountDownLatch(1);
CountDownLatch elkLatch = new CountDownLatch(3);
FlightRecorder.addPeriodicEvent(ElkEvent.class, () -> {
ElkEvent ee = new ElkEvent();
ee.commit();
});
try (RecordingStream s = new RecordingStream()) {
s.onEvent(ElkEvent.class.getName(), e -> {
System.out.println("Found elk!");
elkLatch.countDown();
});
s.onEvent(LionEvent.class.getName(), e -> {
System.out.println("Found lion!");
lionLatch.countDown();
});
s.onEvent(FrogEvent.class.getName(), e -> {
System.out.println("Found frog!");
frogLatch.countDown();
});
s.startAsync();
FrogEvent fe = new FrogEvent();
fe.commit();
LionEvent le = new LionEvent();
le.commit();
frogLatch.await();
lionLatch.await();
elkLatch.await();
}
}
}
/* /*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,39 +23,25 @@ ...@@ -23,39 +23,25 @@
* questions. * questions.
*/ */
package jdk.jfr.consumer; package jdk.jfr.api.consumer.streaming;
import java.util.HashMap; import jdk.jfr.consumer.EventStream;
import java.util.Iterator;
/** /**
* Commonly used data structure for looking up objects given an id (long value) * @test
* * @summary Verifies that it is possible to open a stream when a repository doesn't
* TODO: Implement without using Map and Long objects, to minimize allocation * exists
* * @key jfr
* @param <T> * @library /lib /
* @run main/othervm jdk.jfr.api.consumer.streaming.TestUnstarted
*/ */
final class LongMap<T> implements Iterable<T> { public class TestUnstarted {
private final HashMap<Long, T> map; public static void main(String... args) throws Exception {
try (EventStream es = EventStream.openRepository()) {
LongMap() { es.onEvent(e -> {
map = new HashMap<>(101); // ignore
} });
es.startAsync();
void put(long id, T object) { }
map.put(id, object);
}
T get(long id) {
return map.get(id);
}
@Override
public Iterator<T> iterator() {
return map.values().iterator();
}
Iterator<Long> keys() {
return map.keySet().iterator();
} }
} }
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.event;
import java.util.List;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.test.lib.jfr.Events;
import jdk.test.lib.jfr.SimpleEvent;
/**
* @test
* @summary Tests that a duration is recorded.
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.api.event.TestEventDuration
*/
public class TestEventDuration {
public static int counter;
public static void main(String[] args) throws Exception {
try(Recording r = new Recording()) {
r.start();
SimpleEvent e = new SimpleEvent();
e.begin();
for (int i = 0; i < 10_000;i++) {
counter+=i;
}
e.end();
e.commit();
r.stop();
List<RecordedEvent> events = Events.fromRecording(r);
if (events.get(0).getDuration().toNanos() < 1) {
throw new AssertionError("Expected a duration");
}
}
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.api.recording.time;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.Recording;
import jdk.jfr.consumer.EventStream;
import jdk.test.lib.Asserts;
/**
* @test
* @key jfr
* @summary Test Recording::SetFlushInterval(...) and
* Recording::getFlushInterval()
* @library /lib /
* @run main/othervm jdk.jfr.api.recording.time.TestSetFlushInterval
*/
public class TestSetFlushInterval {
public static void main(String[] args) throws Throwable {
testSetGet();
testSetNull();
testFlush();
}
static void testFlush() throws Exception {
CountDownLatch flush = new CountDownLatch(1);
try (EventStream es = EventStream.openRepository()) {
es.onFlush(() -> {
flush.countDown();
});
es.startAsync();
try (Recording r = new Recording()) {
r.setFlushInterval(Duration.ofSeconds(1));
r.start();
flush.await();
}
}
}
static void testSetNull() {
try (Recording r = new Recording()) {
r.setFlushInterval(null);
Asserts.fail("Expected NullPointerException");
} catch (NullPointerException npe) {
// as expected
}
}
static void testSetGet() {
try (Recording r = new Recording()) {
Duration a = Duration.ofNanos(21378461289374646L);
r.setFlushInterval(a);
Duration b = r.getFlushInterval();
Asserts.assertEQ(a, b);
}
}
}
...@@ -87,6 +87,12 @@ public class TestLookForUntestedEvents { ...@@ -87,6 +87,12 @@ public class TestLookForUntestedEvents {
) )
); );
// Experimental events
private static final Set<String> experimentalEvents = new HashSet<>(
Arrays.asList(
"Flush", "FlushStorage", "FlushStacktrace",
"FlushStringPool", "FlushMetadata", "FlushTypeSet")
);
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) {
...@@ -145,6 +151,10 @@ public class TestLookForUntestedEvents { ...@@ -145,6 +151,10 @@ public class TestLookForUntestedEvents {
} }
} }
// remove experimental events from eventsFromEventNamesClass since jfrEventTypes
// excludes experimental events
eventsFromEventNamesClass.removeAll(experimentalEvents);
if (!jfrEventTypes.equals(eventsFromEventNamesClass)) { if (!jfrEventTypes.equals(eventsFromEventNamesClass)) {
String exceptionMsg = "Events declared in jdk.test.lib.jfr.EventNames differ " + String exceptionMsg = "Events declared in jdk.test.lib.jfr.EventNames differ " +
"from events returned by FlightRecorder.getEventTypes()"; "from events returned by FlightRecorder.getEventTypes()";
......
...@@ -157,8 +157,11 @@ public class TestOptoObjectAllocationsSampling { ...@@ -157,8 +157,11 @@ public class TestOptoObjectAllocationsSampling {
final String arrayObjectClassName = InstanceObject[].class.getName(); final String arrayObjectClassName = InstanceObject[].class.getName();
for (RecordedEvent event : events) { for (RecordedEvent event : events) {
System.out.println(event);
RecordedStackTrace stackTrace = event.getStackTrace(); RecordedStackTrace stackTrace = event.getStackTrace();
Asserts.assertTrue(stackTrace != null); if (stackTrace == null) {
continue;
}
List<RecordedFrame> frames = stackTrace.getFrames(); List<RecordedFrame> frames = stackTrace.getFrames();
Asserts.assertTrue(frames != null && frames.size() > 0); Asserts.assertTrue(frames != null && frames.size() > 0);
RecordedFrame topFrame = frames.get(0); RecordedFrame topFrame = frames.get(0);
......
...@@ -26,15 +26,19 @@ package jdk.jfr.event.oldobject; ...@@ -26,15 +26,19 @@ package jdk.jfr.event.oldobject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; import java.util.concurrent.CyclicBarrier;
import jdk.jfr.Recording; import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedClass; import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedFrame;
import jdk.jfr.consumer.RecordedMethod;
import jdk.jfr.consumer.RecordedObject; import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.internal.test.WhiteBox; import jdk.jfr.internal.test.WhiteBox;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.EventNames; import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events; import jdk.test.lib.jfr.Events;
...@@ -50,13 +54,13 @@ import jdk.test.lib.jfr.Events; ...@@ -50,13 +54,13 @@ import jdk.test.lib.jfr.Events;
public class TestLargeRootSet { public class TestLargeRootSet {
private static final int THREAD_COUNT = 50; private static final int THREAD_COUNT = 50;
private static final Random RANDOM = new Random(4711);
public static Vector<StackObject[]> temporaries = new Vector<>(OldObjects.MIN_SIZE);
private static class RootThread extends Thread { private static class RootThread extends Thread {
private final CyclicBarrier barrier; private final CyclicBarrier barrier;
private int maxDepth = OldObjects.MIN_SIZE / THREAD_COUNT; private int maxDepth = OldObjects.MIN_SIZE / THREAD_COUNT;
public List<StackObject[]> temporaries = new ArrayList<>(maxDepth);
RootThread(CyclicBarrier cb) { RootThread(CyclicBarrier cb) {
this.barrier = cb; this.barrier = cb;
} }
...@@ -67,8 +71,9 @@ public class TestLargeRootSet { ...@@ -67,8 +71,9 @@ public class TestLargeRootSet {
private void buildRootObjects() { private void buildRootObjects() {
if (maxDepth-- > 0) { if (maxDepth-- > 0) {
// Allocate array to trigger sampling code path for interpreter / c1 // Allocate array to trigger sampling code path for interpreter
StackObject[] stackObject = new StackObject[0]; // / c1
StackObject[] stackObject = new StackObject[RANDOM.nextInt(7)];
temporaries.add(stackObject); // make sure object escapes temporaries.add(stackObject); // make sure object escapes
buildRootObjects(); buildRootObjects();
} else { } else {
...@@ -91,37 +96,73 @@ public class TestLargeRootSet { ...@@ -91,37 +96,73 @@ public class TestLargeRootSet {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
WhiteBox.setWriteAllObjectSamples(true); WhiteBox.setWriteAllObjectSamples(true);
int attempt = 1;
List<RootThread> threads = new ArrayList<>(); while (true) {
try (Recording r = new Recording()) { System.out.println();
r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); System.out.println();
r.start(); System.out.println("ATTEMPT: " + attempt);
CyclicBarrier cb = new CyclicBarrier(THREAD_COUNT + 1); System.out.println("====================================");
for (int i = 0; i < THREAD_COUNT; i++) { List<RootThread> threads = new ArrayList<>();
RootThread t = new RootThread(cb); try (Recording r = new Recording()) {
t.start(); r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity");
if (i % 10 == 0) { r.start();
// Give threads some breathing room before starting next batch CyclicBarrier cb = new CyclicBarrier(THREAD_COUNT + 1);
Thread.sleep(100); for (int i = 0; i < THREAD_COUNT; i++) {
RootThread t = new RootThread(cb);
t.start();
if (i % 10 == 0) {
// Give threads some breathing room before starting next
// batch
Thread.sleep(100);
}
threads.add(t);
} }
threads.add(t); cb.await();
} System.gc();
cb.await(); r.stop();
System.gc(); cb.await();
r.stop(); List<RecordedEvent> events = Events.fromRecording(r);
cb.await(); Events.hasEvents(events);
List<RecordedEvent> events = Events.fromRecording(r); int sample = 0;
Events.hasEvents(events); for (RecordedEvent e : events) {
for (RecordedEvent e : events) { RecordedObject ro = e.getValue("object");
RecordedObject ro = e.getValue("object"); RecordedClass rc = ro.getValue("type");
RecordedClass rc = ro.getValue("type"); System.out.println("Sample: " + sample);
System.out.println(rc.getName()); System.out.println(" - allocationTime: " + e.getInstant("allocationTime"));
if (rc.getName().equals(StackObject[].class.getName())) { System.out.println(" - type: " + rc.getName());
return; // ok RecordedObject root = e.getValue("root");
if (root != null) {
System.out.println(" - root:");
System.out.println(" - description: " + root.getValue("description"));
System.out.println(" - system: " + root.getValue("system"));
System.out.println(" - type: " + root.getValue("type"));
} else {
System.out.println(" - root: N/A");
}
RecordedStackTrace stack = e.getStackTrace();
if (stack != null) {
System.out.println(" - stack:");
int frameCount = 0;
for (RecordedFrame frame : stack.getFrames()) {
RecordedMethod m = frame.getMethod();
System.out.println(" " + m.getType().getName() + "." + m.getName() + "(...)");
frameCount++;
if (frameCount == 10) {
break;
}
}
} else {
System.out.println(" - stack: N/A");
}
System.out.println();
if (rc.getName().equals(StackObject[].class.getName())) {
return; // ok
}
sample++;
} }
} }
Asserts.fail("Could not find root object"); attempt++;
} }
} }
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.util.concurrent.CountDownLatch;
import java.util.HashMap;
import java.util.Map;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Period;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordingStream;
import jdk.jfr.consumer.RecordedEvent;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.EventNames;
/**
* @test
* @summary Verifies at the metalevel that stream contents are written to ongoing recordings
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.event.runtime.TestFlush
*/
public class TestFlush {
private static boolean flushEventAck = false;
@Period("2 s")
static class ZebraEvent extends Event {
}
static class CatEvent extends Event {
}
static class DogEvent extends Event {
}
static class MouseEvent extends Event {
}
public static void main(String... args) throws InterruptedException {
CountDownLatch dogLatch = new CountDownLatch(1);
CountDownLatch catLatch = new CountDownLatch(1);
CountDownLatch mouseLatch = new CountDownLatch(1);
CountDownLatch zebraLatch = new CountDownLatch(3);
FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
ZebraEvent ze = new ZebraEvent();
ze.commit();
});
try (RecordingStream rs = new RecordingStream()) {
rs.enable(EventNames.Flush);
rs.enable(EventNames.FlushStorage);
rs.enable(EventNames.FlushStacktrace);
rs.enable(EventNames.FlushStringPool);
rs.enable(EventNames.FlushMetadata);
rs.enable(EventNames.FlushTypeSet);
rs.onEvent(e -> {
switch (e.getEventType().getName()) {
case EventNames.Flush:
flushEventAck = true;
case EventNames.FlushStorage:
case EventNames.FlushStacktrace:
case EventNames.FlushStringPool:
case EventNames.FlushMetadata:
case EventNames.FlushTypeSet:
validateFlushEvent(e);
return;
}
if (e.getEventType().getName().equals(CatEvent.class.getName())) {
System.out.println("Found cat!");
catLatch.countDown();
return;
}
if (e.getEventType().getName().equals(DogEvent.class.getName())) {
System.out.println("Found dog!");
dogLatch.countDown();
return;
}
if (e.getEventType().getName().equals(ZebraEvent.class.getName())) {
System.out.println("Found zebra!");
zebraLatch.countDown();
return;
}
if (e.getEventType().getName().equals(MouseEvent.class.getName())) {
System.out.println("Found mouse!");
mouseLatch.countDown();
return;
}
System.out.println("Unexpected event: " + e.getEventType().getName());
});
rs.startAsync();
try (Recording r1 = new Recording()) {
r1.start();
MouseEvent me = new MouseEvent();
me.commit();
System.out.println("Mouse emitted");
mouseLatch.await();
try (Recording r2 = new Recording()) { // force chunk rotation in stream
r2.start();
DogEvent de = new DogEvent();
de.commit();
System.out.println("Dog emitted");
dogLatch.await();
CatEvent ce = new CatEvent();
ce.commit();
System.out.println("Cat emitted");
catLatch.await();
zebraLatch.await();
acknowledgeFlushEvent();
}
}
}
}
private static void printEvent(RecordedEvent re) {
System.out.println(re.getEventType().getName());
System.out.println(re.getStartTime().toEpochMilli());
System.out.println(re.getEndTime().toEpochMilli());
}
private static void printFlushEvent(RecordedEvent re) {
printEvent(re);
System.out.println("flushID: " + (long) re.getValue("flushId"));
System.out.println("elements: " + (long) re.getValue("elements"));
System.out.println("size: " + (long) re.getValue("size"));
}
private static void validateFlushEvent(RecordedEvent re) {
printFlushEvent(re);
Asserts.assertTrue(re.getEventType().getName().contains("Flush"), "invalid Event type");
Asserts.assertGT((long) re.getValue("flushId"), 0L, "Invalid flush ID");
Asserts.assertGT((long) re.getValue("elements"), 0L, "No elements");
Asserts.assertGT((long) re.getValue("size"), 0L, "Empty size");
}
private static void acknowledgeFlushEvent() {
Asserts.assertTrue(flushEventAck, "No Flush event");
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.jcmd;
import java.time.Duration;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Recording;
/**
* @test
* @summary Start a recording with a flush interval
* @key jfr
* @library /lib /
* @run main/othervm jdk.jfr.jcmd.TestJcmdStartReadOnlyFile
*/
public class TestJcmdStartFlushInterval {
public static void main(String[] args) throws Exception {
JcmdHelper.jcmd("JFR.start","flush-interval=1s");
for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) {
Duration d = r.getFlushInterval();
if (d.equals(Duration.ofSeconds(1))) {
return; //OK
} else {
throw new Exception("Unexpected flush-interval=" + d);
}
}
throw new Exception("No recording found");
}
}
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.time.Duration;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.JVM;
import jdk.jfr.Recording;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
import static jdk.test.lib.Asserts.assertTrue;
/**
* @test
* @key jfr
* @library /lib /
* @modules jdk.jfr/jdk.jfr.internal
* @run main/othervm jdk.jfr.jvm.TestThreadExclusion
*/
/**
* Starts and stops a number of threads in order.
* Verifies that events are in the same order.
*/
public class TestThreadExclusion {
private final static String EVENT_NAME_THREAD_START = EventNames.ThreadStart;
private final static String EVENT_NAME_THREAD_END = EventNames.ThreadEnd;
private static final String THREAD_NAME_PREFIX = "TestThread-";
private static JVM jvm;
public static void main(String[] args) throws Throwable {
// Test Java Thread Start event
Recording recording = new Recording();
recording.enable(EVENT_NAME_THREAD_START).withThreshold(Duration.ofMillis(0));
recording.enable(EVENT_NAME_THREAD_END).withThreshold(Duration.ofMillis(0));
recording.start();
LatchedThread[] threads = startThreads();
long[] javaThreadIds = getJavaThreadIds(threads);
stopThreads(threads);
recording.stop();
List<RecordedEvent> events = Events.fromRecording(recording);
verifyThreadExclusion(events, javaThreadIds);
}
private static void verifyThreadExclusion(List<RecordedEvent> events, long[] javaThreadIds) throws Exception {
for (RecordedEvent event : events) {
System.out.println("Event:" + event);
final long eventJavaThreadId = event.getThread().getJavaThreadId();
for (int i = 0; i < javaThreadIds.length; ++i) {
if (eventJavaThreadId == javaThreadIds[i]) {
throw new Exception("Event " + event.getEventType().getName() + " has a thread id " + eventJavaThreadId + " that should have been excluded");
}
}
}
}
private static LatchedThread[] startThreads() {
LatchedThread threads[] = new LatchedThread[10];
ThreadGroup threadGroup = new ThreadGroup("TestThreadGroup");
jvm = JVM.getJVM();
for (int i = 0; i < threads.length; i++) {
threads[i] = new LatchedThread(threadGroup, THREAD_NAME_PREFIX + i);
jvm.exclude(threads[i]);
threads[i].startThread();
System.out.println("Started thread id=" + threads[i].getId());
}
return threads;
}
private static long[] getJavaThreadIds(LatchedThread[] threads) {
long[] javaThreadIds = new long[threads.length];
for (int i = 0; i < threads.length; ++i) {
javaThreadIds[i] = threads[i].getId();
}
return javaThreadIds;
}
private static void stopThreads(LatchedThread[] threads) {
for (LatchedThread thread : threads) {
assertTrue(jvm.isExcluded(thread), "Thread " + thread + "should be excluded");
thread.stopThread();
while (thread.isAlive()) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private static class LatchedThread extends Thread {
private final CountDownLatch start = new CountDownLatch(1);
private final CountDownLatch stop = new CountDownLatch(1);
public LatchedThread(ThreadGroup threadGroup, String name) {
super(threadGroup, name);
}
public void run() {
start.countDown();
try {
stop.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void startThread() {
this.start();
try {
start.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void stopThread() {
stop.countDown();
}
}
}
/* /*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -30,8 +30,10 @@ import java.io.IOException; ...@@ -30,8 +30,10 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.jfr.AnnotationElement; import jdk.jfr.AnnotationElement;
import jdk.jfr.Configuration; import jdk.jfr.Configuration;
...@@ -48,6 +50,7 @@ import jdk.jfr.Recording; ...@@ -48,6 +50,7 @@ import jdk.jfr.Recording;
import jdk.jfr.RecordingState; import jdk.jfr.RecordingState;
import jdk.jfr.SettingControl; import jdk.jfr.SettingControl;
import jdk.jfr.ValueDescriptor; import jdk.jfr.ValueDescriptor;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordedClass; import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedFrame; import jdk.jfr.consumer.RecordedFrame;
...@@ -57,6 +60,7 @@ import jdk.jfr.consumer.RecordedStackTrace; ...@@ -57,6 +60,7 @@ import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.consumer.RecordedThread; import jdk.jfr.consumer.RecordedThread;
import jdk.jfr.consumer.RecordedThreadGroup; import jdk.jfr.consumer.RecordedThreadGroup;
import jdk.jfr.consumer.RecordingFile; import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.consumer.RecordingStream;
import jdk.management.jfr.ConfigurationInfo; import jdk.management.jfr.ConfigurationInfo;
import jdk.management.jfr.EventTypeInfo; import jdk.management.jfr.EventTypeInfo;
import jdk.management.jfr.FlightRecorderMXBean; import jdk.management.jfr.FlightRecorderMXBean;
...@@ -105,9 +109,11 @@ public class TestUnsupportedVM { ...@@ -105,9 +109,11 @@ public class TestUnsupportedVM {
RecordingState.class, RecordingState.class,
SettingControl.class, SettingControl.class,
SettingDescriptorInfo.class, SettingDescriptorInfo.class,
ValueDescriptor.class ValueDescriptor.class,
EventStream.class,
RecordingStream.class
}; };
// * @run main/othervm -Dprepare-recording=true jdk.jfr.jvm.TestUnsupportedVM
@Label("My Event") @Label("My Event")
@Description("My fine event") @Description("My fine event")
static class MyEvent extends Event { static class MyEvent extends Event {
...@@ -124,7 +130,7 @@ public class TestUnsupportedVM { ...@@ -124,7 +130,7 @@ public class TestUnsupportedVM {
return; return;
} }
System.out.println("jdk.jfr.unsupportedvm=" + System.getProperty("jdk.jfr.unsupportedvm")); System.out.println("jfr.unsupported.vm=" + System.getProperty("jfr.unsupported.vm"));
// Class FlightRecorder // Class FlightRecorder
if (FlightRecorder.isAvailable()) { if (FlightRecorder.isAvailable()) {
throw new AssertionError("JFR should not be available on an unsupported VM"); throw new AssertionError("JFR should not be available on an unsupported VM");
...@@ -135,6 +141,7 @@ public class TestUnsupportedVM { ...@@ -135,6 +141,7 @@ public class TestUnsupportedVM {
} }
assertIllegalStateException(() -> FlightRecorder.getFlightRecorder()); assertIllegalStateException(() -> FlightRecorder.getFlightRecorder());
assertIllegalStateException(() -> new RecordingStream());
assertSwallow(() -> FlightRecorder.addListener(new FlightRecorderListener() {})); assertSwallow(() -> FlightRecorder.addListener(new FlightRecorderListener() {}));
assertSwallow(() -> FlightRecorder.removeListener(new FlightRecorderListener() {})); assertSwallow(() -> FlightRecorder.removeListener(new FlightRecorderListener() {}));
assertSwallow(() -> FlightRecorder.register(MyEvent.class)); assertSwallow(() -> FlightRecorder.register(MyEvent.class));
...@@ -173,8 +180,39 @@ public class TestUnsupportedVM { ...@@ -173,8 +180,39 @@ public class TestUnsupportedVM {
// Only run this part of tests if we are on VM // Only run this part of tests if we are on VM
// that can produce a recording file // that can produce a recording file
if (Files.exists(RECORDING_FILE)) { if (Files.exists(RECORDING_FILE)) {
boolean firstFileEvent = true;
for(RecordedEvent re : RecordingFile.readAllEvents(RECORDING_FILE)) { for(RecordedEvent re : RecordingFile.readAllEvents(RECORDING_FILE)) {
System.out.println(re); // Print one event
if (firstFileEvent) {
System.out.println(re);
firstFileEvent = false;
}
}
AtomicBoolean firstStreamEvent = new AtomicBoolean(true);
try (EventStream es = EventStream.openFile(RECORDING_FILE)) {
es.onEvent(e -> {
// Print one event
if (firstStreamEvent.get()) {
try {
System.out.println(e);
firstStreamEvent.set(false);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
es.start();
if (firstStreamEvent.get()) {
throw new AssertionError("Didn't print streaming event");
}
}
try (EventStream es = EventStream.openRepository()) {
es.onEvent(e -> {
System.out.println(e);
});
es.startAsync();
es.awaitTermination(Duration.ofMillis(10));
} }
} }
} }
......
/*
* Copyright (c) 2019, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.startupargs;
import java.time.Duration;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Recording;
/**
* @test
* @summary Start a recording with a flush interval
* @key jfr
* @library /lib /
* @run main/othervm -XX:StartFlightRecording=flush-interval=1s jdk.jfr.startupargs.TestFlushInterval
*/
public class TestFlushInterval {
public static void main(String[] args) throws Exception {
for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) {
Duration d = r.getFlushInterval();
if (d.equals(Duration.ofSeconds(1))) {
return; //OK
} else {
throw new Exception("Unexpected flush-interval " + d);
}
}
throw new Exception("No recording found");
}
}
...@@ -175,6 +175,12 @@ public class EventNames { ...@@ -175,6 +175,12 @@ public class EventNames {
public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter"; public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter";
public final static String ActiveRecording = PREFIX + "ActiveRecording"; public final static String ActiveRecording = PREFIX + "ActiveRecording";
public final static String ActiveSetting = PREFIX + "ActiveSetting"; public final static String ActiveSetting = PREFIX + "ActiveSetting";
public static final String Flush = PREFIX + "Flush";
public static final String FlushStringPool = PREFIX + "FlushStringPool";
public static final String FlushStacktrace = PREFIX + "FlushStacktrace";
public static final String FlushStorage = PREFIX + "FlushStorage";
public static final String FlushMetadata = PREFIX + "FlushMetadata";
public static final String FlushTypeSet = PREFIX + "FlushTypeSet";
public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation"; public final static String OptoInstanceObjectAllocation = PREFIX + "OptoInstanceObjectAllocation";
public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation"; public final static String OptoArrayObjectAllocation = PREFIX + "OptoArrayObjectAllocation";
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册