--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/AbstractEventStream.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,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.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.Comparator;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.Consumer;
-
-import jdk.jfr.internal.JVM;
-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. In particular, it handles:
- *
- * - configuration storage
- * - atomic updates to a configuration
- * - dispatch mechanism
- * - error handling
- * - security
- *
- */
-abstract class AbstractEventStream implements EventStream {
-
- static final Comparator<? super RecordedEvent> END_TIME = (e1, e2) -> Long.compare(e1.endTimeTicks, e2.endTimeTicks);
-
- 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;
-
- public 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.toNanosPart();
- 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 Exception;
-
- protected final void setClosed(boolean closed) {
- this.closed = closed;
- }
-
- protected final boolean isClosed() {
- return closed;
- }
-
- protected final void startAsync(long startNanos) {
- startInternal(startNanos);
- Runnable r = () -> run(accessControllerContext);
- thread = SecuritySupport.createThreadWitNoPermissions(nextThreadName(), r);
- thread.start();
- }
-
- protected 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.setStartNanos(startNanos);
- }
- configuration.setStarted(true);
- }
- }
-
- private void execute() {
- JVM.getJVM().exclude(Thread.currentThread());
- 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
- } catch (Exception e) {
- // TODO: Remove before integrating
- e.printStackTrace();
- } 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;
- }
-}
\ No newline at end of file
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,436 +0,0 @@
-/*
- * 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.consumer;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-import java.util.StringJoiner;
-
-import jdk.jfr.EventType;
-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;
-import jdk.jfr.internal.consumer.ChunkHeader;
-import jdk.jfr.internal.consumer.InternalEventFilter;
-import jdk.jfr.internal.consumer.Parser;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Parses a chunk.
- *
- */
-final class ChunkParser {
-
- static final class ParserConfiguration {
- final boolean reuse;
- final boolean ordered;
- final InternalEventFilter eventFilter;
-
- long filterStart;
- long filterEnd;
-
- public ParserConfiguration(long filterStart, long filterEnd, boolean reuse, boolean ordered, InternalEventFilter 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, InternalEventFilter.ACCEPT_ALL);
- }
- }
-
- // Checkpoint that finishes a flush segment
- static final byte CHECKPOINT_FLUSH_MASK = 1;
- // Checkpoint contains chunk header information in the first pool
- static final byte CHECKPOINT_CHUNK_HEADER_MASK = 2;
- // Checkpoint contains only statics that will not change from chunk to chunk
- static final byte CHECKPOINT_STATICS_MASK = 4;
- // Checkpoint contains thread related information
- static final byte CHECKPOINT_THREADS_MASK = 8;
-
- 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 long pollInterval;
- 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());
- }
-
- public ChunkParser(RecordingInput input, ParserConfiguration pc) throws IOException {
- this(new ChunkHeader(input), null, pc);
- }
-
- public 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.pollInterval = 1000;
- this.constantLookups = new LongMap<>();
- this.previousMetadata = null;
- } else {
- this.constantLookups = previous.constantLookups;
- this.previousMetadata = previous.metadata;
- this.pollInterval = previous.pollInterval;
- 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);
- }
-
- public 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.
- */
- public 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();
-
- if (typeId != 0) { // Not metadata event
- Parser p = parsers.get(typeId);
- if (p instanceof EventParser) {
- EventParser ep = (EventParser) p;
- RecordedEvent event = ep.parse(input);
- if (event != null) {
- input.position(pos + size);
- return event;
- }
- }
- if (typeId == 1 && flushOperation != null) { // checkpoint event
- parseCheckpoint();
- }
- }
- 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 c = input.readByte();
- if ((c & CHECKPOINT_FLUSH_MASK)== 1) {
- 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(pollInterval);
- }
- }
-
- 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();
- }
-
- public 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();
- }
-
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantLookup.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,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.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);
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantMap.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,191 +0,0 @@
-/*
- * 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.consumer;
-
-import jdk.jfr.internal.LongMap;
-
-/**
- * Holds mapping between a set of keys and their corresponding object.
- *
- * If the type is a known type, i.e. {@link RecordedThread}, an
- * {@link ObjectFactory} can be supplied which will instantiate a typed object.
- */
-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 long key;
- private final ConstantMap pool;
-
- Reference(ConstantMap pool, long key) {
- this.pool = pool;
- this.key = key;
- }
-
- Object resolve() {
- return pool.get(key);
- }
-
- public String toString() {
- return "ref: " + pool.name + "[" + key + "]";
- }
- }
-
- final ObjectFactory<?> factory;
- final String name;
-
- private final LongMap<Object> objects;
-
- private boolean resolving;
- private boolean allResolved;
-
- private ConstantMap() {
- this(null, "<empty>");
- allResolved = true;
- }
-
- ConstantMap(ObjectFactory<?> factory, String name) {
- this.name = name;
- this.objects = new LongMap<>(2);
- this.factory = factory;
- }
-
- Object get(long id) {
- // fast path, all objects in pool resolved
- if (allResolved) {
- return objects.get(id);
- }
- // referenced from a pool, deal with this later
- if (!resolving) {
- return new Reference(this, 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");
- }
-
- // id is resolved (but not the whole pool)
- if (objects.isSetId(id, RESOLUTION_FINISHED)) {
- return value;
- }
-
- // resolving ourself, abort to avoid infinite recursion
- if (objects.isSetId(id, RESOLUTION_STARTED)) {
- return null;
- }
-
- // mark id as STARTED if we should
- // come back during object resolution
- objects.setId(id, RESOLUTION_STARTED);
-
- // 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) {
- Object factorized = factory.createObject(id, resolved);
- objects.put(id, factorized);
- return factorized;
- } else {
- objects.put(id, resolved);
- return resolved;
- }
- }
-
- private static Object resolve(Object o) {
- if (o instanceof Reference) {
- return resolve(((Reference) o).resolve());
- }
- if (o != null && o.getClass().isArray()) {
- final Object[] array = (Object[]) o;
- for (int i = 0; i < array.length; i++) {
- Object element = array[i];
- array[i] = resolve(element);
- }
- return array;
- }
- return o;
- }
-
- public void resolve() {
- objects.forEachKey(k -> get(k));
- }
-
- public void put(long key, Object value) {
- objects.put(key, value);
- }
-
- public void setResolving() {
- resolving = true;
- allResolved = false;
- }
-
- public void setResolved() {
- allResolved = true;
- resolving = false;
- }
-
- public String getName() {
- 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;
- }
-
- public boolean isResolved(long id) {
- if (objects.hasKey(id)) {
- return objects.isSetId(id, RESOLUTION_FINISHED);
- }
- return false;
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/Dispatcher.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,144 +0,0 @@
-package jdk.jfr.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.ChunkParser.ParserConfiguration;
-import jdk.jfr.internal.LongMap;
-import jdk.jfr.internal.consumer.InternalEventFilter;
-
-public final class Dispatcher {
-
- public final static class EventDispatcher {
- final static EventDispatcher[] NO_DISPATCHERS = new EventDispatcher[0];
- final String eventName;
- final Consumer<RecordedEvent> action;
-
- public EventDispatcher(Consumer<RecordedEvent> action) {
- this(null, action);
- }
-
- public EventDispatcher(String eventName, Consumer<RecordedEvent> action) {
- this.eventName = eventName;
- this.action = action;
- }
-
- public void offer(RecordedEvent event) {
- action.accept(event);
- }
-
- public boolean accepts(EventType eventType) {
- return (eventName == null || eventType.getName().equals(eventName));
- }
- }
-
- final Consumer<Throwable>[] errorActions;
- final Runnable[] flushActions;
- final Runnable[] closeActions;
- final EventDispatcher[] dispatchers;
- 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({"unchecked","rawtypes"})
- 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;
- }
-
- private static InternalEventFilter buildFilter(EventDispatcher[] dispatchers) {
- InternalEventFilter ef = new InternalEventFilter();
- for (EventDispatcher ed : dispatchers) {
- String name = ed.eventName;
- if (name == null) {
- return InternalEventFilter.ACCEPT_ALL;
- }
- ef.setThreshold(name, 0);
- }
- return ef;
- }
-
- protected final 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;
- }
- for (int i = 0; i < dispatchers.length; i++) {
- try {
- dispatchers[i].offer(event);
- } catch (Exception e) {
- handleError(e);
- }
- }
- }
-
- public 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> conusmer = (Consumer<Throwable>) consumers[i];
- conusmer.accept(e);
- }
- }
-
- 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);
- }
- }
- }
-
- void defaultErrorHandler(Throwable e) {
- e.printStackTrace();
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,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.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.Objects;
-
-import jdk.jfr.consumer.ChunkParser.ParserConfiguration;
-import jdk.jfr.internal.Utils;
-import jdk.jfr.internal.consumer.FileAccess;
-import jdk.jfr.internal.consumer.RecordingInput;
-import jdk.jfr.internal.consumer.RepositoryFiles;
-
-/**
- * Implementation of an {@code EventStream}} that operates against a directory
- * with chunk files.
- *
- */
-final class EventDirectoryStream extends AbstractEventStream {
- private final RepositoryFiles repositoryFiles;
- private final boolean active;
- private final FileAccess fileAccess;
-
- private ChunkParser chunkParser;
- private long chunkStartNanos;
- private RecordedEvent[] sortedList;
-
- 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 Exception {
- 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;
- }
- chunkStartNanos = repositoryFiles.getTimestamp(path);
- try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
- chunkParser = new ChunkParser(input, disp.parserConfiguration);
- long segmentStart = chunkParser.getStartNanos() + chunkParser.getChunkDuration();
- long filterStart = validStartTime ? disp.startNanos : segmentStart;
- long filterEnd = disp.endTime != null ? disp.endNanos: Long.MAX_VALUE;
-
- while (!isClosed()) {
- boolean awaitnewEvent = false;
- while (!isClosed() && !chunkParser.isChunkFinished()) {
- disp = dispatcher();
- ParserConfiguration pc = disp.parserConfiguration;
- pc.filterStart = filterStart;
- pc.filterEnd = filterEnd;
- chunkParser.updateConfiguration(pc, true);
- chunkParser.setFlushOperation(getFlushOperation());
- if (pc.ordered) {
- awaitnewEvent = processOrdered(disp, awaitnewEvent);
- } else {
- awaitnewEvent = processUnordered(disp, awaitnewEvent);
- }
- if (chunkParser.getStartNanos() + chunkParser.getChunkDuration() > filterEnd) {
- close();
- return;
- }
- }
-
- if (isClosed()) {
- return;
- }
- long durationNanos = chunkParser.getChunkDuration();
- path = repositoryFiles.nextPath(chunkStartNanos + durationNanos);
- if (path == null) {
- return; // stream closed
- }
- chunkStartNanos = repositoryFiles.getTimestamp(path);
- input.setFile(path);
- chunkParser = chunkParser.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 (sortedList == null) {
- sortedList = new RecordedEvent[100_000];
- }
- int index = 0;
- while (true) {
- RecordedEvent e = chunkParser.readStreamingEvent(awaitNewEvents);
- if (e == null) {
- // wait for new event with next call to
- // readStreamingEvent()
- awaitNewEvents = true;
- break;
- }
- awaitNewEvents = false;
- if (index == sortedList.length) {
- sortedList = Arrays.copyOf(sortedList, sortedList.length * 2);
- }
- sortedList[index++] = e;
- }
-
- // no events found
- if (index == 0 && chunkParser.isChunkFinished()) {
- return awaitNewEvents;
- }
- // at least 2 events, sort them
- if (index > 1) {
- Arrays.sort(sortedList, 0, index, END_TIME);
- }
- for (int i = 0; i < index; i++) {
- c.dispatch(sortedList[i]);
- }
- return awaitNewEvents;
- }
-
- private boolean processUnordered(Dispatcher c, boolean awaitNewEvents) throws IOException {
- while (true) {
- RecordedEvent e = chunkParser.readStreamingEvent(awaitNewEvents);
- if (e == null) {
- return true;
- } else {
- c.dispatch(e);
- }
- }
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventFileStream.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,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.consumer;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.security.AccessControlContext;
-import java.util.Arrays;
-import java.util.Objects;
-
-import jdk.jfr.internal.consumer.FileAccess;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Implementation of an event stream that operates against a recording file.
- *
- */
-final class EventFileStream extends AbstractEventStream {
- private final RecordingInput input;
- private ChunkParser chunkParser;
- private RecordedEvent[] sortedList;
-
- 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;
- }
-
- chunkParser = new ChunkParser(input, disp.parserConfiguration);
- while (!isClosed()) {
- if (chunkParser.getStartNanos() > end) {
- close();
- return;
- }
- disp = dispatcher();
- disp.parserConfiguration.filterStart = start;
- disp.parserConfiguration.filterEnd = end;
- chunkParser.updateConfiguration(disp.parserConfiguration, true);
- chunkParser.setFlushOperation(getFlushOperation());
- if (disp.parserConfiguration.ordered) {
- processOrdered(disp);
- } else {
- processUnordered(disp);
- }
- if (isClosed() || chunkParser.isLastChunk()) {
- return;
- }
- chunkParser = chunkParser.nextChunkParser();
- }
- }
-
- private void processOrdered(Dispatcher c) throws IOException {
- if (sortedList == null) {
- sortedList = new RecordedEvent[10_000];
- }
- RecordedEvent event;
- int index = 0;
- while (true) {
- event = chunkParser.readEvent();
- if (event == null) {
- Arrays.sort(sortedList, 0, index, END_TIME);
- for (int i = 0; i < index; i++) {
- c.dispatch(sortedList[i]);
- }
- return;
- }
- if (index == sortedList.length) {
- RecordedEvent[] tmp = sortedList;
- sortedList = new RecordedEvent[2 * tmp.length];
- System.arraycopy(tmp, 0, sortedList, 0, tmp.length);
- }
- sortedList[index++] = event;
- }
- }
-
- private void processUnordered(Dispatcher c) throws IOException {
- while (!isClosed()) {
- RecordedEvent event = chunkParser.readEvent();
- if (event == null) {
- return;
- }
- c.dispatch(event);
- }
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventFilter.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,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.consumer;
-
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.List;
-
-final class EventFilter {
- private final String[] eventNames;
- private final Duration threshold;
- private final String[] fields;
-
- private EventFilter(String[] eventNames, Duration threshold, String[] fields) {
- this.eventNames = eventNames;
- this.threshold = threshold;
- this.fields = fields;
- }
-
- public static EventFilter eventTypes(String... eventNames) {
- return new EventFilter(eventNames.clone(), null, new String[0]);
- }
-
- public EventFilter aboveThreshold(Duration threshold) {
- return new EventFilter(eventNames, threshold, fields);
- }
-
- public EventFilter mustHaveFields(String... fieldNames) {
- return new EventFilter(eventNames, threshold, fieldNames);
- }
-
- public EventFilter onlyThreads(Thread... t) {
- return this;
- }
-
- public EventFilter onlyThreadIds(long... threadId) {
- return this;
- }
-
- public EventFilter onlyThreadNames(String... threadName) {
- return this;
- }
-
- public EventFilter threadFilters(String... filter) {
- return this;
- }
-
- List<String> getFields() {
- return Arrays.asList(fields);
- }
-
- List<String> getEventNames() {
- return Arrays.asList(fields);
- }
-
- Duration getThreshold() {
- return threshold;
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-/*
- * 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.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.internal.consumer.Parser;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Parses an event and returns a {@link RecordedEvent}.
- *
- */
-final class EventParser extends Parser {
- 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 = new RecordedEvent(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 = new RecordedEvent(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();
- event.startTimeTicks = startTicks;
- event.endTimeTicks = endTicks;
- Object[] values = event.objects;
- 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 new RecordedEvent(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;
- }
-
- public 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;
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java Mon Sep 16 09:45:22 2019 +0200
@@ -36,6 +36,8 @@
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;
/**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectContext.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,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.consumer;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import jdk.jfr.EventType;
-import jdk.jfr.ValueDescriptor;
-
-final class ObjectContext {
- private final Map<ValueDescriptor, ObjectContext> contextLookup;
-
- final EventType eventType;
- final List<ValueDescriptor> fields;
- final TimeConverter timeConverter;
-
- public ObjectContext(EventType eventType, List<ValueDescriptor> fields, TimeConverter timeConverter) {
- this.contextLookup = new HashMap<>();
- this.eventType = eventType;
- this.fields = fields;
- this.timeConverter = timeConverter;
- }
-
- private ObjectContext(ObjectContext parent, ValueDescriptor descriptor) {
- this.eventType = parent.eventType;
- this.contextLookup = parent.contextLookup;
- this.timeConverter = parent.timeConverter;
- this.fields = descriptor.getFields();
- }
-
- public ObjectContext getInstance(ValueDescriptor descriptor) {
- ObjectContext context = contextLookup.get(descriptor);
- if (context == null) {
- context = new ObjectContext(this, descriptor);
- contextLookup.put(descriptor, context);
- }
- return context;
- }
-}
\ No newline at end of file
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * 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.consumer;
-
-import jdk.jfr.internal.Type;
-
-/**
- * Abstract factory for creating specialized types
- */
-abstract class ObjectFactory<T> {
-
- final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
- final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
- final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
- final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
-
- public static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
- switch (type.getName()) {
- case "java.lang.Thread":
- return RecordedThread.createFactory(type, timeConverter);
- 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 ObjectContext objectContext;
-
- 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);
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ParserFactory.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,380 +0,0 @@
-/*
- * 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.consumer;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import jdk.jfr.EventType;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.LongMap;
-import jdk.jfr.internal.MetadataDescriptor;
-import jdk.jfr.internal.PrivateAccess;
-import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.Parser;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Class that create parsers suitable for reading events and constant pools
- */
-final class ParserFactory {
- private final LongMap<Parser> parsers = new LongMap<>();
- private final TimeConverter timeConverter;
- private final LongMap<Type> types = new LongMap<>();
- private final LongMap<ConstantLookup> constantLookups;
-
- public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException {
- this.constantLookups = constantLookups;
- this.timeConverter = timeConverter;
- for (Type t : metadata.getTypes()) {
- types.put(t.getId(), t);
- }
- List<Type> typeList = new ArrayList<>();
- types.forEach(typeList::add);
- for (Type t : typeList) {
- if (!t.getFields().isEmpty()) { // Avoid primitives
- CompositeParser cp = createCompositeParser(t, false);
- if (t.isSimpleType()) { // Reduce to nested parser
- parsers.put(t.getId(), cp.parsers[0]);
- }
-
- }
- }
- // Override event types with event parsers
- for (EventType t : metadata.getEventTypes()) {
- parsers.put(t.getId(), createEventParser(t));
- }
- }
-
- public LongMap<Parser> getParsers() {
- return parsers;
- }
-
- public LongMap<Type> getTypeMap() {
- return types;
- }
-
- private EventParser createEventParser(EventType eventType) throws IOException {
- List<Parser> parsers = new ArrayList<Parser>();
- for (ValueDescriptor f : eventType.getFields()) {
- parsers.add(createParser(f, true));
- }
- return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
- }
-
- private Parser createParser(ValueDescriptor v, boolean event) throws IOException {
- boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
- if (v.isArray()) {
- Type valueType = PrivateAccess.getInstance().getType(v);
- ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
- return new ArrayParser(createParser(element, event));
- }
- long id = v.getTypeId();
- Type type = types.get(id);
- if (type == null) {
- throw new IOException("Type '" + v.getTypeName() + "' is not defined");
- }
- if (constantPool) {
- ConstantLookup lookup = constantLookups.get(id);
- if (lookup == null) {
- ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- lookup = new ConstantLookup(pool, type);
- constantLookups.put(id, lookup);
- }
- if (event) {
- return new EventValueConstantParser(lookup);
- }
- return new ConstantValueParser(lookup);
- }
- Parser parser = parsers.get(id);
- if (parser == null) {
- if (!v.getFields().isEmpty()) {
- return createCompositeParser(type, event);
- } else {
- return registerParserType(type, createPrimitiveParser(type, constantPool));
- }
- }
- return parser;
- }
-
- private Parser createPrimitiveParser(Type type, boolean event) throws IOException {
- switch (type.getName()) {
- case "int":
- return new IntegerParser();
- case "long":
- return new LongParser();
- case "float":
- return new FloatParser();
- case "double":
- return new DoubleParser();
- case "char":
- return new CharacterParser();
- case "boolean":
- return new BooleanParser();
- case "short":
- return new ShortParser();
- case "byte":
- return new ByteParser();
- case "java.lang.String":
- ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
- ConstantLookup lookup = new ConstantLookup(pool, type);
- constantLookups.put(type.getId(), lookup);
- return new StringParser(lookup, event);
- default:
- throw new IOException("Unknown primitive type " + type.getName());
- }
- }
-
- private Parser registerParserType(Type t, Parser parser) {
- Parser p = parsers.get(t.getId());
- // check if parser exists (known type)
- if (p != null) {
- return p;
- }
- parsers.put(t.getId(), parser);
- return parser;
- }
-
- private CompositeParser createCompositeParser(Type type, boolean event) throws IOException {
- List<ValueDescriptor> vds = type.getFields();
- Parser[] parsers = new Parser[vds.size()];
- CompositeParser composite = new CompositeParser(parsers);
- // need to pre-register so recursive types can be handled
- registerParserType(type, composite);
-
- int index = 0;
- for (ValueDescriptor vd : vds) {
- parsers[index++] = createParser(vd, event);
- }
- return composite;
- }
-
- private static final class BooleanParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- 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 {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Byte.valueOf(input.readByte());
- }
-
- @Override
- public void skip(RecordingInput input) throws IOException {
- input.skipBytes(1);
- }
- }
-
- private static final class LongParser extends Parser {
- private Object lastLongObject = Long.valueOf(0);
- private long last = 0;
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- 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 Integer lastIntegergObject = Integer.valueOf(0);
- private int last = 0;
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- 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();
- }
- }
-
- private static final class ShortParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Short.valueOf(input.readShort());
- }
-
- @Override
- public void skip(RecordingInput input) throws IOException {
- input.readShort();
- }
- }
-
- private static final class CharacterParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Character.valueOf(input.readChar());
- }
-
- @Override
- public void skip(RecordingInput input) throws IOException {
- input.readChar();
- }
- }
-
- private static final class FloatParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Float.valueOf(input.readFloat());
- }
-
- @Override
- public void skip(RecordingInput input) throws IOException {
- input.skipBytes(Float.SIZE);
- }
- }
-
- private static final class DoubleParser extends Parser {
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return Double.valueOf(input.readDouble());
- }
-
- @Override
- public void skip(RecordingInput input) throws IOException {
- input.skipBytes(Double.SIZE);
- }
- }
-
- private final static class ArrayParser extends Parser {
- private final Parser elementParser;
-
- public ArrayParser(Parser elementParser) {
- this.elementParser = elementParser;
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- final int size = input.readInt();
- final Object[] array = new Object[size];
- for (int i = 0; i < size; i++) {
- array[i] = elementParser.parse(input);
- }
- 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);
- }
- }
- }
-
- final static class CompositeParser extends Parser {
- private final Parser[] parsers;
-
- public CompositeParser(Parser[] valueParsers) {
- this.parsers = valueParsers;
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- final Object[] values = new Object[parsers.length];
- for (int i = 0; i < values.length; i++) {
- values[i] = parsers[i].parse(input);
- }
- return values;
- }
-
- @Override
- public void skip(RecordingInput input) throws IOException {
- for (int i = 0; i < parsers.length; i++) {
- parsers[i].skip(input);
- }
- }
- }
-
- public static final class EventValueConstantParser extends Parser {
- private final ConstantLookup lookup;
- private Object lastValue = 0;
- private long lastKey = -1;
- EventValueConstantParser(ConstantLookup lookup) {
- this.lookup = lookup;
- }
-
- @Override
- 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();
- }
- }
-
- public static final class ConstantValueParser extends Parser {
- private final ConstantLookup lookup;
- ConstantValueParser(ConstantLookup lookup) {
- this.lookup = lookup;
- }
-
- @Override
- public Object parse(RecordingInput input) throws IOException {
- return lookup.getCurrent(input.readLong());
- }
-
- @Override
- public void skip(RecordingInput input) throws IOException {
- input.readLong();
- }
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java Mon Sep 16 09:45:22 2019 +0200
@@ -27,7 +27,7 @@
import java.lang.reflect.Modifier;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded Java type, such as a class or an interface.
@@ -35,20 +35,10 @@
* @since 9
*/
public final class RecordedClass extends RecordedObject {
-
- static ObjectFactory<RecordedClass> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedClass>(type, timeConverter) {
- @Override
- RecordedClass createTyped(ObjectContext objectContext, long id, Object[] values) {
- return new RecordedClass(objectContext, id, values);
- }
- };
- }
-
private final long uniqueId;
// package private
- private RecordedClass(ObjectContext objectContext, long id, Object[] values) {
+ RecordedClass(ObjectContext objectContext, long id, Object[] values) {
super(objectContext, values);
this.uniqueId = id;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java Mon Sep 16 09:45:22 2019 +0200
@@ -25,7 +25,7 @@
package jdk.jfr.consumer;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded Java class loader.
@@ -33,20 +33,10 @@
* @since 9
*/
public final class RecordedClassLoader extends RecordedObject {
-
- static ObjectFactory<RecordedClassLoader> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedClassLoader>(type, timeConverter) {
- @Override
- RecordedClassLoader createTyped(ObjectContext objectContext, long id, Object[] values) {
- return new RecordedClassLoader(objectContext, id, values);
- }
- };
- }
-
private final long uniqueId;
// package private
- private RecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
+ RecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
super(objectContext, values);
this.uniqueId = id;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java Mon Sep 16 09:45:22 2019 +0200
@@ -32,6 +32,7 @@
import jdk.jfr.EventType;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.EventInstrumentation;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded event.
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java Mon Sep 16 09:45:22 2019 +0200
@@ -27,7 +27,7 @@
import java.lang.reflect.Modifier;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded frame in a stack trace.
@@ -35,19 +35,9 @@
* @since 9
*/
public final class RecordedFrame extends RecordedObject {
-
- static ObjectFactory<RecordedFrame> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedFrame>(type, timeConverter) {
- @Override
- RecordedFrame createTyped(ObjectContext objectContext, long id, Object[] values) {
- return new RecordedFrame(objectContext, values);
- }
- };
- }
-
// package private
- RecordedFrame(ObjectContext objectContext, Object[] objects) {
- super(objectContext, objects);
+ RecordedFrame(ObjectContext objectContext, Object[] values) {
+ super(objectContext, values);
}
/**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java Mon Sep 16 09:45:22 2019 +0200
@@ -27,7 +27,7 @@
import java.lang.reflect.Modifier;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded method.
@@ -36,16 +36,8 @@
*/
public final class RecordedMethod extends RecordedObject {
- static ObjectFactory<RecordedMethod> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedMethod>(type, timeConverter) {
- @Override
- RecordedMethod createTyped(ObjectContext objectContext, long id, Object[] values) {
- return new RecordedMethod(objectContext, values);
- }
- };
- }
-
- private RecordedMethod(ObjectContext objectContext, Object[] values) {
+ // package private
+ RecordedMethod(ObjectContext objectContext, Object[] values) {
super(objectContext, values);
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java Mon Sep 16 09:45:22 2019 +0200
@@ -31,17 +31,18 @@
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
-import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import jdk.jfr.Timespan;
import jdk.jfr.Timestamp;
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.Type;
-import jdk.jfr.internal.consumer.Parser;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.ObjectContext;
import jdk.jfr.internal.tool.PrettyWriter;
/**
@@ -57,7 +58,7 @@
public class RecordedObject {
static{
- RecordingInternals.INSTANCE = new RecordingInternals() {
+ JdkJfrConsumer access = new JdkJfrConsumer() {
public List<Type> readTypes(RecordingFile file) throws IOException {
return file.readTypes();
}
@@ -72,15 +73,71 @@
}
@Override
- public void sort(List<RecordedEvent> events) {
- Collections.sort(events, (e1, e2) -> Long.compare(e1.endTimeTicks, e2.endTimeTicks));
+ 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 Parser newStringParser() {
- return new StringParser(null, false);
+ 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 {
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java Mon Sep 16 09:45:22 2019 +0200
@@ -29,7 +29,7 @@
import java.util.Collections;
import java.util.List;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded stack trace.
@@ -37,17 +37,8 @@
* @since 9
*/
public final class RecordedStackTrace extends RecordedObject {
-
- static ObjectFactory<RecordedStackTrace> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedStackTrace>(type, timeConverter) {
- @Override
- RecordedStackTrace createTyped(ObjectContext objectContext, long id, Object[] values) {
- return new RecordedStackTrace(objectContext, values);
- }
- };
- }
-
- private RecordedStackTrace(ObjectContext objectContext, Object[] values) {
+ // package private
+ RecordedStackTrace(ObjectContext objectContext, Object[] values) {
super(objectContext, values);
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java Mon Sep 16 09:45:22 2019 +0200
@@ -25,7 +25,7 @@
package jdk.jfr.consumer;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded thread.
@@ -33,19 +33,10 @@
* @since 9
*/
public final class RecordedThread extends RecordedObject {
-
- static ObjectFactory<RecordedThread> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedThread>(type, timeConverter) {
- @Override
- RecordedThread createTyped(ObjectContext objectContext, long id, Object[] values) {
- return new RecordedThread(objectContext, id, values);
- }
- };
- }
-
private final long uniqueId;
- private RecordedThread(ObjectContext objectContext, long id, Object[] values) {
+ // package private
+ RecordedThread(ObjectContext objectContext, long id, Object[] values) {
super(objectContext, values);
this.uniqueId = id;
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java Mon Sep 16 09:45:22 2019 +0200
@@ -25,7 +25,7 @@
package jdk.jfr.consumer;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
/**
* A recorded Java thread group.
@@ -33,17 +33,8 @@
* @since 9
*/
public final class RecordedThreadGroup extends RecordedObject {
-
- static ObjectFactory<RecordedThreadGroup> createFactory(Type type, TimeConverter timeConverter) {
- return new ObjectFactory<RecordedThreadGroup>(type, timeConverter) {
- @Override
- RecordedThreadGroup createTyped(ObjectContext objectContext, long id, Object[] values) {
- return new RecordedThreadGroup(objectContext, values);
- }
- };
- }
-
- private RecordedThreadGroup(ObjectContext objectContext, Object[] values) {
+ // package private
+ RecordedThreadGroup(ObjectContext objectContext, Object[] values) {
super(objectContext, values);
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java Mon Sep 16 09:45:22 2019 +0200
@@ -39,6 +39,7 @@
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.ChunkParser;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput;
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java Mon Sep 16 09:45:22 2019 +0200
@@ -42,6 +42,7 @@
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
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/StreamConfiguration.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-package jdk.jfr.consumer;
-
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-import jdk.jfr.consumer.Dispatcher.EventDispatcher;
-import jdk.jfr.internal.Utils;
-
-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;
-
- 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.action == 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;
- }
-
- public synchronized void clearChanged() {
- changed = false;
- }
-}
\ No newline at end of file
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/StringParser.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,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.consumer;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-
-import jdk.jfr.internal.consumer.Parser;
-import jdk.jfr.internal.consumer.RecordingInput;
-import jdk.jfr.internal.consumer.StringEncoding;
-
-final class StringParser extends Parser {
- private final static Charset UTF8 = Charset.forName("UTF-8");
- private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
-
- 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];
- }
- }
- }
-
- 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 == StringEncoding.STRING_ENCODING_CONSTANT_POOL) {
- long key = input.readLong();
- if (event) {
- return stringLookup.getCurrentResolved(key);
- } else {
- return stringLookup.getCurrent(key);
- }
- }
- if (encoding == StringEncoding.STRING_ENCODING_NULL) {
- return null;
- }
- if (encoding == StringEncoding.STRING_ENCODING_EMPTY_STRING) {
- return "";
- }
- if (encoding == StringEncoding.STRING_ENCODING_CHAR_ARRAY) {
- return charArrayParser.parse(input);
- }
- if (encoding == StringEncoding.STRING_ENCODING_UTF8_BYTE_ARRAY) {
- return utf8parser.parse(input);
- }
- if (encoding == StringEncoding.STRING_ENCODING_LATIN1_BYTE_ARRAY) {
- 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 == StringEncoding.STRING_ENCODING_CONSTANT_POOL) {
- input.readLong();
- return;
- }
- if (encoding == StringEncoding.STRING_ENCODING_EMPTY_STRING) {
- return;
- }
- if (encoding == StringEncoding.STRING_ENCODING_NULL) {
- return;
- }
- if (encoding == StringEncoding.STRING_ENCODING_CHAR_ARRAY) {
- charArrayParser.skip(input);
- return;
- }
- if (encoding == StringEncoding.STRING_ENCODING_UTF8_BYTE_ARRAY) {
- utf8parser.skip(input);
- return;
- }
- if (encoding == StringEncoding.STRING_ENCODING_LATIN1_BYTE_ARRAY) {
- latin1parser.skip(input);
- return;
- }
- throw new IOException("Unknown string encoding " + encoding);
- }
-}
\ No newline at end of file
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/TimeConverter.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * 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.consumer;
-
-import java.time.DateTimeException;
-import java.time.ZoneOffset;
-
-import jdk.jfr.internal.LogLevel;
-import jdk.jfr.internal.LogTag;
-import jdk.jfr.internal.Logger;
-import jdk.jfr.internal.consumer.ChunkHeader;
-
-/**
- * Converts ticks to nanoseconds
- */
-final class TimeConverter {
- private final long startTicks;
- private final long startNanos;
- private final double divisor;
- private final ZoneOffset zoneOffet;
-
- TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
- this.startTicks = chunkHeader.getStartTicks();
- this.startNanos = chunkHeader.getStartNanos();
- this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
- 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) {
- return startNanos + (long) ((ticks - startTicks) / divisor);
- }
-
- public long convertTimespan(long ticks) {
- return (long) (ticks / divisor);
- }
-
- public ZoneOffset getZoneOffset() {
- return zoneOffet;
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/UseCasesStream.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,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.consumer;
-
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.text.ParseException;
-import java.time.Duration;
-import java.util.ArrayDeque;
-import java.util.Deque;
-
-import jdk.jfr.Configuration;
-import jdk.jfr.EventType;
-import jdk.jfr.ValueDescriptor;
-
-final class UseCasesStream {
-
- //
- // Use case: Out-of-the-Box Experience
- //
- // - Simple things should be simple
- // - Pique interest, i.e. a one-liner on Stack Overflow
- // - Few lines of code as possible
- // - Should be easier than alternative technologies, like JMX and JVM TI
- //
- // - Non-goals: Corner-cases, advanced configuration, releasing resources
- //
- public static void outOfTheBox() throws Exception {
- try (RecordingStream rs = new RecordingStream()) {
- rs.enable("jdk.ExceptionThrown");
- rs.onEvent(e -> System.out.println(e.getString("message")));
- rs.start();
- }
-
- try (RecordingStream rs = new RecordingStream()) {
- rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(20)).withoutStackTrace();
- rs.onEvent(System.out::println);
- rs.start();
- }
-
- try (RecordingStream rs = new RecordingStream()) {
- rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
- rs.onEvent(System.out::println);
- rs.start();
- }
-
- try (RecordingStream rs = new RecordingStream()) {
- rs.enable("jdk.GarbageCollection");
- rs.onEvent(System.out::println);
- rs.start();
- }
- }
-
- // Use case: Event Forwarding
- //
- // - Forward arbitrary event to frameworks such as RxJava, JSON/XML and
- // Kafka
- // - Handle flooding
- // - Performant
- // - Graceful shutdown
- // - Non-goals: Filter events
- //
- public static void eventForwarding() throws InterruptedException, IOException, ParseException {
- // KafkaProducer producer = new KafkaProducer<String, String>();
- try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("default"))) {
- rs.setMaxAge(Duration.ofMinutes(5));
- rs.setMaxSize(1000_000_000L);
- rs.setOrdered(false);
- rs.setReuse(true); // default
- // rs.consume(e -> producer.send(new
- // ProducerRecord<String,String>("topic", e.getString("key"),
- // e.getString("value"))));
- rs.start();
- }
- // Write primitive values to XML
- try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("deafult"))) {
- try (PrintWriter p = new PrintWriter(new FileWriter("recording.xml"))) {
- p.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
- p.println("<events>");
- rs.onEvent(e -> {
- EventType type = e.getEventType();
- p.println(" <event type=\"" + type.getName() + "\" start=\"" + e.getStartTime() + "\" end=\"" + e.getEndTime() + "\">");
- for (ValueDescriptor field : e.getEventType().getFields()) {
- Object value = e.getValue(field.getName());
- if (value instanceof Number || field.getTypeName().equals("java.lang.String")) {
- p.println(" <value field=\"" + field.getName() + "\">" + value + "</value>");
- }
- }
- });
- rs.start();
- p.println("</events>");
- }
- }
- }
-
- // Use case: Repository Access
- //
- // - Read the disk repository from another process, for example a side car
- // in aDocker container
- // - Be able to configure flush interval from command line or jcmd.
- // - Graceful shutdown
- //
- public static void repositoryAccess() throws IOException, InterruptedException {
- Path repository = Paths.get("c:\\repository").toAbsolutePath();
- String command = new String();
- command += "java -XX:StartFlightRecording:flush-interval=2s";
- command += "-XX:FlightRecorderOption:repository=" + repository + " Application";
- Process myProcess = Runtime.getRuntime().exec(command);
- try (RecordingStream rs = new RecordingStream()) {
- rs.onEvent(System.out::println);
- rs.startAsync();
- Thread.sleep(10_000);
- myProcess.destroy();
- Thread.sleep(10_000);
- }
- }
-
- // Use: Tooling
- //
- // - Monitor a stream of data for a very long time
- // - Predictable interval, i.e. once every second
- // - Notification with minimal delay
- // - Events with the same period should arrive together
- // - Consume events in chronological order
- // - Low overhead
- //
- public static void tooling() throws IOException, ParseException {
- Deque<Double> measurements = new ArrayDeque<>();
- try (RecordingStream rs = new RecordingStream(Configuration.getConfiguration("profile"))) {
- rs.setFlushInterval(Duration.ofSeconds(1));
- rs.setMaxAge(Duration.ofMinutes(1));
- rs.setOrdered(true); // default
- rs.setReuse(false);
- rs.onEvent("jdk.CPULoad", e -> {
- double d = e.getDouble("totalMachine");
- measurements.addFirst(d);
- if (measurements.size() > 60) {
- measurements.removeLast();
- }
- // repaint();
- });
- rs.start();
- }
- }
-
- // Use case: Low Impact
- //
- // - Support event subscriptions in a low latency environment (minimal GC
- // pauses)
- // - Filter out relevant events to minimize disk overhead and allocation
- // pressure
- // - Avoid impact from other recordings
- // - Avoid observer effect, in particular self-recursion
- //
- // Non-goals: one-liner
- //
- public static void lowImpact() throws InterruptedException, IOException, ParseException {
- try (RecordingStream rs = new RecordingStream()) {
- rs.setReuse(true); // default
- rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
- rs.enable("jdk.ExceptionThrow");
- rs.onEvent("jdk.JavaMonitorEnter", System.out::println);
- rs.onEvent("jdk.ExceptionThrow", System.out::println);
- rs.start();
- }
- }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java Mon Sep 16 09:45:22 2019 +0200
@@ -49,9 +49,8 @@
import jdk.jfr.SettingDescriptor;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.MetadataDescriptor.Element;
-import jdk.jfr.internal.consumer.Parser;
import jdk.jfr.internal.consumer.RecordingInput;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.StringParser;
/**
* Parses metadata.
@@ -68,7 +67,7 @@
this.input = input;
int size = input.readInt();
this.pool = new ArrayList<>(size);
- Parser p = RecordingInternals.instance().newStringParser();
+ StringParser p = new StringParser(null, false);
for (int i = 0; i < size; i++) {
this.pool.add((String) p.parse(input));
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,285 @@
+/*
+ * 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.JVM;
+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. In particular, it handles:
+ *
+ * - configuration storage
+ * - atomic updates to a configuration
+ * - dispatch mechanism
+ * - error handling
+ * - security
+ *
+ */
+public 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;
+
+ public 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.toNanosPart();
+ 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 Exception;
+
+ 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.setStartNanos(startNanos);
+ }
+ configuration.setStarted(true);
+ }
+ }
+
+ private void execute() {
+ JVM.getJVM().exclude(Thread.currentThread());
+ 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
+ } catch (Exception e) {
+ // TODO: Remove before integrating
+ e.printStackTrace();
+ } 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;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkParser.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,434 @@
+/*
+ * 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 {
+ final boolean reuse;
+ final boolean ordered;
+ final InternalEventFilter eventFilter;
+
+ long filterStart;
+ long filterEnd;
+
+ public ParserConfiguration(long filterStart, long filterEnd, boolean reuse, boolean ordered, InternalEventFilter 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, InternalEventFilter.ACCEPT_ALL);
+ }
+ }
+
+ // Checkpoint that finishes a flush segment
+ static final byte CHECKPOINT_FLUSH_MASK = 1;
+ // Checkpoint contains chunk header information in the first pool
+ static final byte CHECKPOINT_CHUNK_HEADER_MASK = 2;
+ // Checkpoint contains only statics that will not change from chunk to chunk
+ static final byte CHECKPOINT_STATICS_MASK = 4;
+ // Checkpoint contains thread related information
+ static final byte CHECKPOINT_THREADS_MASK = 8;
+
+ 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 long pollInterval;
+ 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());
+ }
+
+ public ChunkParser(RecordingInput input, ParserConfiguration pc) throws IOException {
+ this(new ChunkHeader(input), null, pc);
+ }
+
+ public 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.pollInterval = 1000;
+ this.constantLookups = new LongMap<>();
+ this.previousMetadata = null;
+ } else {
+ this.constantLookups = previous.constantLookups;
+ this.previousMetadata = previous.metadata;
+ this.pollInterval = previous.pollInterval;
+ 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);
+ }
+
+ public 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.
+ */
+ public 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();
+
+ if (typeId != 0) { // Not metadata event
+ Parser p = parsers.get(typeId);
+ if (p instanceof EventParser) {
+ EventParser ep = (EventParser) p;
+ RecordedEvent event = ep.parse(input);
+ if (event != null) {
+ input.position(pos + size);
+ return event;
+ }
+ }
+ if (typeId == 1 && flushOperation != null) { // checkpoint event
+ parseCheckpoint();
+ }
+ }
+ 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 c = input.readByte();
+ if ((c & CHECKPOINT_FLUSH_MASK)== 1) {
+ 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(pollInterval);
+ }
+ }
+
+ 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();
+ }
+
+ public 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();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantLookup.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,62 @@
+/*
+ * 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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantMap.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,191 @@
+/*
+ * 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.internal.LongMap;
+
+/**
+ * Holds mapping between a set of keys and their corresponding object.
+ *
+ * If the type is a known type, i.e. {@link RecordedThread}, an
+ * {@link ObjectFactory} can be supplied which will instantiate a typed object.
+ */
+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 long key;
+ private final ConstantMap pool;
+
+ Reference(ConstantMap pool, long key) {
+ this.pool = pool;
+ this.key = key;
+ }
+
+ Object resolve() {
+ return pool.get(key);
+ }
+
+ public String toString() {
+ return "ref: " + pool.name + "[" + key + "]";
+ }
+ }
+
+ final ObjectFactory<?> factory;
+ final String name;
+
+ private final LongMap<Object> objects;
+
+ private boolean resolving;
+ private boolean allResolved;
+
+ private ConstantMap() {
+ this(null, "<empty>");
+ allResolved = true;
+ }
+
+ ConstantMap(ObjectFactory<?> factory, String name) {
+ this.name = name;
+ this.objects = new LongMap<>(2);
+ this.factory = factory;
+ }
+
+ Object get(long id) {
+ // fast path, all objects in pool resolved
+ if (allResolved) {
+ return objects.get(id);
+ }
+ // referenced from a pool, deal with this later
+ if (!resolving) {
+ return new Reference(this, 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");
+ }
+
+ // id is resolved (but not the whole pool)
+ if (objects.isSetId(id, RESOLUTION_FINISHED)) {
+ return value;
+ }
+
+ // resolving ourself, abort to avoid infinite recursion
+ if (objects.isSetId(id, RESOLUTION_STARTED)) {
+ return null;
+ }
+
+ // mark id as STARTED if we should
+ // come back during object resolution
+ objects.setId(id, RESOLUTION_STARTED);
+
+ // 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) {
+ Object factorized = factory.createObject(id, resolved);
+ objects.put(id, factorized);
+ return factorized;
+ } else {
+ objects.put(id, resolved);
+ return resolved;
+ }
+ }
+
+ private static Object resolve(Object o) {
+ if (o instanceof Reference) {
+ return resolve(((Reference) o).resolve());
+ }
+ if (o != null && o.getClass().isArray()) {
+ final Object[] array = (Object[]) o;
+ for (int i = 0; i < array.length; i++) {
+ Object element = array[i];
+ array[i] = resolve(element);
+ }
+ return array;
+ }
+ return o;
+ }
+
+ public void resolve() {
+ objects.forEachKey(k -> get(k));
+ }
+
+ public void put(long key, Object value) {
+ objects.put(key, value);
+ }
+
+ public void setResolving() {
+ resolving = true;
+ allResolved = false;
+ }
+
+ public void setResolved() {
+ allResolved = true;
+ resolving = false;
+ }
+
+ public String getName() {
+ 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;
+ }
+
+ public boolean isResolved(long id) {
+ if (objects.hasKey(id)) {
+ return objects.isSetId(id, RESOLUTION_FINISHED);
+ }
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/Dispatcher.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,144 @@
+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 {
+
+ public final static class EventDispatcher {
+ final static EventDispatcher[] NO_DISPATCHERS = new EventDispatcher[0];
+ final String eventName;
+ public final Consumer<RecordedEvent> action;
+
+ public EventDispatcher(Consumer<RecordedEvent> action) {
+ this(null, action);
+ }
+
+ public EventDispatcher(String eventName, Consumer<RecordedEvent> action) {
+ this.eventName = eventName;
+ this.action = action;
+ }
+
+ public void offer(RecordedEvent event) {
+ action.accept(event);
+ }
+
+ public boolean accepts(EventType eventType) {
+ return (eventName == null || eventType.getName().equals(eventName));
+ }
+ }
+
+ final Consumer<Throwable>[] errorActions;
+ final Runnable[] flushActions;
+ final Runnable[] closeActions;
+ final EventDispatcher[] dispatchers;
+ 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({"unchecked","rawtypes"})
+ 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;
+ }
+
+ private static InternalEventFilter buildFilter(EventDispatcher[] dispatchers) {
+ InternalEventFilter ef = new InternalEventFilter();
+ for (EventDispatcher ed : dispatchers) {
+ String name = ed.eventName;
+ if (name == null) {
+ return InternalEventFilter.ACCEPT_ALL;
+ }
+ ef.setThreshold(name, 0);
+ }
+ return ef;
+ }
+
+ protected final 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;
+ }
+ for (int i = 0; i < dispatchers.length; i++) {
+ try {
+ dispatchers[i].offer(event);
+ } catch (Exception e) {
+ handleError(e);
+ }
+ }
+ }
+
+ public 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> conusmer = (Consumer<Throwable>) consumers[i];
+ conusmer.accept(e);
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ void defaultErrorHandler(Throwable e) {
+ e.printStackTrace();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,183 @@
+/*
+ * 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.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 chunkParser;
+ private long chunkStartNanos;
+ private RecordedEvent[] sortedList;
+
+ 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 Exception {
+ 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;
+ }
+ chunkStartNanos = repositoryFiles.getTimestamp(path);
+ try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
+ chunkParser = new ChunkParser(input, disp.parserConfiguration);
+ long segmentStart = chunkParser.getStartNanos() + chunkParser.getChunkDuration();
+ long filterStart = validStartTime ? disp.startNanos : segmentStart;
+ long filterEnd = disp.endTime != null ? disp.endNanos: Long.MAX_VALUE;
+
+ while (!isClosed()) {
+ boolean awaitnewEvent = false;
+ while (!isClosed() && !chunkParser.isChunkFinished()) {
+ disp = dispatcher();
+ ParserConfiguration pc = disp.parserConfiguration;
+ pc.filterStart = filterStart;
+ pc.filterEnd = filterEnd;
+ chunkParser.updateConfiguration(pc, true);
+ chunkParser.setFlushOperation(getFlushOperation());
+ if (pc.ordered) {
+ awaitnewEvent = processOrdered(disp, awaitnewEvent);
+ } else {
+ awaitnewEvent = processUnordered(disp, awaitnewEvent);
+ }
+ if (chunkParser.getStartNanos() + chunkParser.getChunkDuration() > filterEnd) {
+ close();
+ return;
+ }
+ }
+
+ if (isClosed()) {
+ return;
+ }
+ long durationNanos = chunkParser.getChunkDuration();
+ path = repositoryFiles.nextPath(chunkStartNanos + durationNanos);
+ if (path == null) {
+ return; // stream closed
+ }
+ chunkStartNanos = repositoryFiles.getTimestamp(path);
+ input.setFile(path);
+ chunkParser = chunkParser.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 (sortedList == null) {
+ sortedList = new RecordedEvent[100_000];
+ }
+ int index = 0;
+ while (true) {
+ RecordedEvent e = chunkParser.readStreamingEvent(awaitNewEvents);
+ if (e == null) {
+ // wait for new event with next call to
+ // readStreamingEvent()
+ awaitNewEvents = true;
+ break;
+ }
+ awaitNewEvents = false;
+ if (index == sortedList.length) {
+ sortedList = Arrays.copyOf(sortedList, sortedList.length * 2);
+ }
+ sortedList[index++] = e;
+ }
+
+ // no events found
+ if (index == 0 && chunkParser.isChunkFinished()) {
+ return awaitNewEvents;
+ }
+ // at least 2 events, sort them
+ if (index > 1) {
+ Arrays.sort(sortedList, 0, index, EVENT_COMPARATOR);
+ }
+ for (int i = 0; i < index; i++) {
+ c.dispatch(sortedList[i]);
+ }
+ return awaitNewEvents;
+ }
+
+ private boolean processUnordered(Dispatcher c, boolean awaitNewEvents) throws IOException {
+ while (true) {
+ RecordedEvent e = chunkParser.readStreamingEvent(awaitNewEvents);
+ if (e == null) {
+ return true;
+ } else {
+ c.dispatch(e);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,146 @@
+/*
+ * 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 chunkParser;
+ private RecordedEvent[] sortedList;
+
+ 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;
+ }
+
+ chunkParser = new ChunkParser(input, disp.parserConfiguration);
+ while (!isClosed()) {
+ if (chunkParser.getStartNanos() > end) {
+ close();
+ return;
+ }
+ disp = dispatcher();
+ disp.parserConfiguration.filterStart = start;
+ disp.parserConfiguration.filterEnd = end;
+ chunkParser.updateConfiguration(disp.parserConfiguration, true);
+ chunkParser.setFlushOperation(getFlushOperation());
+ if (disp.parserConfiguration.ordered) {
+ processOrdered(disp);
+ } else {
+ processUnordered(disp);
+ }
+ if (isClosed() || chunkParser.isLastChunk()) {
+ return;
+ }
+ chunkParser = chunkParser.nextChunkParser();
+ }
+ }
+
+ private void processOrdered(Dispatcher c) throws IOException {
+ if (sortedList == null) {
+ sortedList = new RecordedEvent[10_000];
+ }
+ RecordedEvent event;
+ int index = 0;
+ while (true) {
+ event = chunkParser.readEvent();
+ if (event == null) {
+ Arrays.sort(sortedList, 0, index, EVENT_COMPARATOR);
+ for (int i = 0; i < index; i++) {
+ c.dispatch(sortedList[i]);
+ }
+ return;
+ }
+ if (index == sortedList.length) {
+ RecordedEvent[] tmp = sortedList;
+ sortedList = new RecordedEvent[2 * tmp.length];
+ System.arraycopy(tmp, 0, sortedList, 0, tmp.length);
+ }
+ sortedList[index++] = event;
+ }
+ }
+
+ private void processUnordered(Dispatcher c) throws IOException {
+ while (!isClosed()) {
+ RecordedEvent event = chunkParser.readEvent();
+ if (event == null) {
+ return;
+ }
+ c.dispatch(event);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventParser.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,203 @@
+/*
+ * 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}.
+ *
+ */
+public 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();
+ PRIVATE_ACCESS.setStartTicks(event, startTicks);
+ PRIVATE_ACCESS.setEndTicks(event, endTicks);
+ Object[] values = PRIVATE_ACCESS.eventValues(event);
+ for (int i = 0; i < values.length; i++) {
+ values[i] = parsers[startIndex + i].parse(input);
+ }
+// event.startTimeTicks = startTicks;
+// event.endTimeTicks = endTicks;
+// Object[] values = event.objects;
+// for (int i = 0; i < values.length; i++) {
+//
+// }
+ 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;
+ }
+
+ public 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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/JdkJfrConsumer.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,101 @@
+/*
+ * 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.util.Comparator;
+import java.util.List;
+
+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;
+/*
+ * Purpose of this class is to give package private access to
+ * the jdk.jfr.consumer package
+ */
+public abstract class JdkJfrConsumer {
+
+ private static JdkJfrConsumer instance;
+
+ // Initialization will trigger setAccess being called
+ private static void forceInitializetion() {
+ try {
+ Class<?> c = RecordedObject.class;
+ Class.forName(c.getName(), true, c.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new InternalError("Should not happen");
+ }
+
+ }
+
+ public static void setAccess(JdkJfrConsumer access) {
+ instance = access;
+ }
+
+ public static JdkJfrConsumer instance() {
+ if (instance == null) {
+ forceInitializetion();
+ }
+ return instance;
+ }
+
+ 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);
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ObjectContext.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,63 @@
+/*
+ * 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.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+
+public final class ObjectContext {
+ private final Map<ValueDescriptor, ObjectContext> contextLookup;
+
+ public final EventType eventType;
+ public final List<ValueDescriptor> fields;
+ public final TimeConverter timeConverter;
+
+ public ObjectContext(EventType eventType, List<ValueDescriptor> fields, TimeConverter timeConverter) {
+ this.contextLookup = new HashMap<>();
+ this.eventType = eventType;
+ this.fields = fields;
+ this.timeConverter = timeConverter;
+ }
+
+ private ObjectContext(ObjectContext parent, ValueDescriptor descriptor) {
+ this.eventType = parent.eventType;
+ this.contextLookup = parent.contextLookup;
+ this.timeConverter = parent.timeConverter;
+ this.fields = descriptor.getFields();
+ }
+
+ public ObjectContext getInstance(ValueDescriptor descriptor) {
+ ObjectContext context = contextLookup.get(descriptor);
+ if (context == null) {
+ context = new ObjectContext(this, descriptor);
+ contextLookup.put(descriptor, context);
+ }
+ return context;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ObjectFactory.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,154 @@
+/*
+ * 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();
+
+ public final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
+ public 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";
+
+ public 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;
+
+ 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);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,380 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.LongMap;
+import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Class that create parsers suitable for reading events and constant pools
+ */
+final class ParserFactory {
+ private final LongMap<Parser> parsers = new LongMap<>();
+ private final TimeConverter timeConverter;
+ private final LongMap<Type> types = new LongMap<>();
+ private final LongMap<ConstantLookup> constantLookups;
+
+ public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException {
+ this.constantLookups = constantLookups;
+ this.timeConverter = timeConverter;
+ for (Type t : metadata.getTypes()) {
+ types.put(t.getId(), t);
+ }
+ List<Type> typeList = new ArrayList<>();
+ types.forEach(typeList::add);
+ for (Type t : typeList) {
+ if (!t.getFields().isEmpty()) { // Avoid primitives
+ CompositeParser cp = createCompositeParser(t, false);
+ if (t.isSimpleType()) { // Reduce to nested parser
+ parsers.put(t.getId(), cp.parsers[0]);
+ }
+
+ }
+ }
+ // Override event types with event parsers
+ for (EventType t : metadata.getEventTypes()) {
+ parsers.put(t.getId(), createEventParser(t));
+ }
+ }
+
+ public LongMap<Parser> getParsers() {
+ return parsers;
+ }
+
+ public LongMap<Type> getTypeMap() {
+ return types;
+ }
+
+ private EventParser createEventParser(EventType eventType) throws IOException {
+ List<Parser> parsers = new ArrayList<Parser>();
+ for (ValueDescriptor f : eventType.getFields()) {
+ parsers.add(createParser(f, true));
+ }
+ return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
+ }
+
+ private Parser createParser(ValueDescriptor v, boolean event) throws IOException {
+ boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
+ if (v.isArray()) {
+ Type valueType = PrivateAccess.getInstance().getType(v);
+ ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
+ return new ArrayParser(createParser(element, event));
+ }
+ long id = v.getTypeId();
+ Type type = types.get(id);
+ if (type == null) {
+ throw new IOException("Type '" + v.getTypeName() + "' is not defined");
+ }
+ if (constantPool) {
+ ConstantLookup lookup = constantLookups.get(id);
+ if (lookup == null) {
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+ lookup = new ConstantLookup(pool, type);
+ constantLookups.put(id, lookup);
+ }
+ if (event) {
+ return new EventValueConstantParser(lookup);
+ }
+ return new ConstantValueParser(lookup);
+ }
+ Parser parser = parsers.get(id);
+ if (parser == null) {
+ if (!v.getFields().isEmpty()) {
+ return createCompositeParser(type, event);
+ } else {
+ return registerParserType(type, createPrimitiveParser(type, constantPool));
+ }
+ }
+ return parser;
+ }
+
+ private Parser createPrimitiveParser(Type type, boolean event) throws IOException {
+ switch (type.getName()) {
+ case "int":
+ return new IntegerParser();
+ case "long":
+ return new LongParser();
+ case "float":
+ return new FloatParser();
+ case "double":
+ return new DoubleParser();
+ case "char":
+ return new CharacterParser();
+ case "boolean":
+ return new BooleanParser();
+ case "short":
+ return new ShortParser();
+ case "byte":
+ return new ByteParser();
+ case "java.lang.String":
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+ ConstantLookup lookup = new ConstantLookup(pool, type);
+ constantLookups.put(type.getId(), lookup);
+ return new StringParser(lookup, event);
+ default:
+ throw new IOException("Unknown primitive type " + type.getName());
+ }
+ }
+
+ private Parser registerParserType(Type t, Parser parser) {
+ Parser p = parsers.get(t.getId());
+ // check if parser exists (known type)
+ if (p != null) {
+ return p;
+ }
+ parsers.put(t.getId(), parser);
+ return parser;
+ }
+
+ private CompositeParser createCompositeParser(Type type, boolean event) throws IOException {
+ List<ValueDescriptor> vds = type.getFields();
+ Parser[] parsers = new Parser[vds.size()];
+ CompositeParser composite = new CompositeParser(parsers);
+ // need to pre-register so recursive types can be handled
+ registerParserType(type, composite);
+
+ int index = 0;
+ for (ValueDescriptor vd : vds) {
+ parsers[index++] = createParser(vd, event);
+ }
+ return composite;
+ }
+
+ private static final class BooleanParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ 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 {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Byte.valueOf(input.readByte());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(1);
+ }
+ }
+
+ private static final class LongParser extends Parser {
+ private Object lastLongObject = Long.valueOf(0);
+ private long last = 0;
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ 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 Integer lastIntegergObject = Integer.valueOf(0);
+ private int last = 0;
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ 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();
+ }
+ }
+
+ private static final class ShortParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Short.valueOf(input.readShort());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readShort();
+ }
+ }
+
+ private static final class CharacterParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Character.valueOf(input.readChar());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readChar();
+ }
+ }
+
+ private static final class FloatParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Float.valueOf(input.readFloat());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(Float.SIZE);
+ }
+ }
+
+ private static final class DoubleParser extends Parser {
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return Double.valueOf(input.readDouble());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.skipBytes(Double.SIZE);
+ }
+ }
+
+ private final static class ArrayParser extends Parser {
+ private final Parser elementParser;
+
+ public ArrayParser(Parser elementParser) {
+ this.elementParser = elementParser;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ final int size = input.readInt();
+ final Object[] array = new Object[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = elementParser.parse(input);
+ }
+ 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);
+ }
+ }
+ }
+
+ final static class CompositeParser extends Parser {
+ private final Parser[] parsers;
+
+ public CompositeParser(Parser[] valueParsers) {
+ this.parsers = valueParsers;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ final Object[] values = new Object[parsers.length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = parsers[i].parse(input);
+ }
+ return values;
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ for (int i = 0; i < parsers.length; i++) {
+ parsers[i].skip(input);
+ }
+ }
+ }
+
+ public static final class EventValueConstantParser extends Parser {
+ private final ConstantLookup lookup;
+ private Object lastValue = 0;
+ private long lastKey = -1;
+ EventValueConstantParser(ConstantLookup lookup) {
+ this.lookup = lookup;
+ }
+
+ @Override
+ 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();
+ }
+ }
+
+ public static final class ConstantValueParser extends Parser {
+ private final ConstantLookup lookup;
+ ConstantValueParser(ConstantLookup lookup) {
+ this.lookup = lookup;
+ }
+
+ @Override
+ public Object parse(RecordingInput input) throws IOException {
+ return lookup.getCurrent(input.readLong());
+ }
+
+ @Override
+ public void skip(RecordingInput input) throws IOException {
+ input.readLong();
+ }
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInternals.java Fri Sep 13 18:46:07 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,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.internal.consumer;
-
-import java.io.IOException;
-import java.util.List;
-
-import jdk.jfr.consumer.RecordedEvent;
-import jdk.jfr.consumer.RecordedObject;
-import jdk.jfr.consumer.RecordingFile;
-import jdk.jfr.internal.Type;
-
-public abstract class RecordingInternals {
-
- public static RecordingInternals instance() {
- if (INSTANCE == null) {
- // Force initialization
- try {
- Class<?> c = RecordedObject.class;
- Class.forName(c.getName(), true, c.getClassLoader());
- } catch (ClassNotFoundException e) {
- throw new InternalError("Should not happen");
- }
- }
- return INSTANCE;
- }
-
- 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);
-
- public abstract Parser newStringParser();
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StreamConfiguration.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StreamConfiguration.java Mon Sep 16 09:45:22 2019 +0200
@@ -1,25 +1,103 @@
-/*
- * 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.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;
+
+ 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.action == 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;
+ }
+
+ public synchronized void clearChanged() {
+ changed = false;
+ }
+}
\ No newline at end of file
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringEncoding.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringEncoding.java Mon Sep 16 09:45:22 2019 +0200
@@ -30,5 +30,4 @@
public final static byte STRING_ENCODING_UTF8_BYTE_ARRAY = (byte) 3;
public final static byte STRING_ENCODING_CHAR_ARRAY = (byte) 4;
public final static byte STRING_ENCODING_LATIN1_BYTE_ARRAY = (byte) 5;
-
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringEncoding;
+
+public final class StringParser extends Parser {
+ private final static Charset UTF8 = Charset.forName("UTF-8");
+ private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
+
+ 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];
+ }
+ }
+ }
+
+ 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 == StringEncoding.STRING_ENCODING_CONSTANT_POOL) {
+ long key = input.readLong();
+ if (event) {
+ return stringLookup.getCurrentResolved(key);
+ } else {
+ return stringLookup.getCurrent(key);
+ }
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_NULL) {
+ return null;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_EMPTY_STRING) {
+ return "";
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_CHAR_ARRAY) {
+ return charArrayParser.parse(input);
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_UTF8_BYTE_ARRAY) {
+ return utf8parser.parse(input);
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_LATIN1_BYTE_ARRAY) {
+ 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 == StringEncoding.STRING_ENCODING_CONSTANT_POOL) {
+ input.readLong();
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_EMPTY_STRING) {
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_NULL) {
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_CHAR_ARRAY) {
+ charArrayParser.skip(input);
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_UTF8_BYTE_ARRAY) {
+ utf8parser.skip(input);
+ return;
+ }
+ if (encoding == StringEncoding.STRING_ENCODING_LATIN1_BYTE_ARRAY) {
+ latin1parser.skip(input);
+ return;
+ }
+ throw new IOException("Unknown string encoding " + encoding);
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/TimeConverter.java Mon Sep 16 09:45:22 2019 +0200
@@ -0,0 +1,72 @@
+/*
+ * 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.time.DateTimeException;
+import java.time.ZoneOffset;
+
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.consumer.ChunkHeader;
+
+/**
+ * Converts ticks to nanoseconds
+ */
+public final class TimeConverter {
+ private final long startTicks;
+ private final long startNanos;
+ private final double divisor;
+ private final ZoneOffset zoneOffet;
+
+ TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
+ this.startTicks = chunkHeader.getStartTicks();
+ this.startNanos = chunkHeader.getStartNanos();
+ this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
+ 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) {
+ return startNanos + (long) ((ticks - startTicks) / divisor);
+ }
+
+ public long convertTimespan(long ticks) {
+ return (long) (ticks / divisor);
+ }
+
+ public ZoneOffset getZoneOffset() {
+ return zoneOffet;
+ }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java Mon Sep 16 09:45:22 2019 +0200
@@ -30,6 +30,7 @@
import java.io.PrintWriter;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -42,7 +43,7 @@
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedObject;
import jdk.jfr.consumer.RecordingFile;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
abstract class EventPrintWriter extends StructuredWriter {
@@ -52,6 +53,7 @@
protected static final String STACK_TRACE_FIELD = "stackTrace";
protected static final String EVENT_THREAD_FIELD = "eventThread";
+ private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
private Predicate<EventType> eventFilter = x -> true;
private int stackDepth;
@@ -74,8 +76,8 @@
if (acceptEvent(event)) {
events.add(event);
}
- if (RecordingInternals.INSTANCE.isLastEventInChunk(file)) {
- RecordingInternals.INSTANCE.sort(events);
+ if (PRIVATE_ACCESS.isLastEventInChunk(file)) {
+ Collections.sort(events, PRIVATE_ACCESS.eventComparator());
print(events);
events.clear();
}
@@ -121,7 +123,7 @@
case TIMESPAN:
return object.getDuration(v.getName());
case TIMESTAMP:
- return RecordingInternals.INSTANCE.getOffsetDataTime(object, v.getName());
+ return PRIVATE_ACCESS.getOffsetDataTime(object, v.getName());
default:
return object.getValue(v.getName());
}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java Fri Sep 13 18:46:07 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java Mon Sep 16 09:45:22 2019 +0200
@@ -35,10 +35,12 @@
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
final class Metadata extends Command {
+ private final static JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
+
private static class TypeComparator implements Comparator<Type> {
@Override
@@ -89,6 +91,7 @@
}
}
+
@Override
public String getName() {
return "metadata";
@@ -125,7 +128,7 @@
PrettyWriter prettyWriter = new PrettyWriter(pw);
prettyWriter.setShowIds(showIds);
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());
for (Type type : types) {
prettyWriter.printType(type);