--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriter.java Tue May 15 20:24:34 2018 +0200
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import jdk.internal.misc.Unsafe;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Class must reside in a package with package restriction.
+ *
+ * Users should not have direct access to underlying memory.
+ *
+ */
+public final class EventWriter {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private final static JVM jvm = JVM.getJVM();
+
+ private long startPosition;
+ private long startPositionAddress;
+ private long currentPosition;
+ private long maxPosition;
+ private final long threadID;
+ private PlatformEventType eventType;
+ private int maxEventSize;
+ private boolean started;
+ private boolean valid;
+ private boolean flushOnEnd;
+ // set by the JVM, not private to avoid being optimized out
+ boolean notified;
+
+ public static EventWriter getEventWriter() {
+ EventWriter ew = (EventWriter)JVM.getEventWriter();
+ return ew != null ? ew : JVM.newEventWriter();
+ }
+
+ public void putBoolean(boolean i) {
+ if (isValidForSize(Byte.BYTES)) {
+ currentPosition += Bits.putBoolean(currentPosition, i);
+ }
+ }
+
+ public void putByte(byte i) {
+ if (isValidForSize(Byte.BYTES)) {
+ unsafe.putByte(currentPosition, i);
+ ++currentPosition;
+ }
+ }
+
+ public void putChar(char v) {
+ if (isValidForSize(Character.BYTES + 1)) {
+ putUncheckedLong(v);
+ }
+ }
+
+ private void putUncheckedChar(char v) {
+ putUncheckedLong(v);
+ }
+
+ public void putShort(short v) {
+ if (isValidForSize(Short.BYTES + 1)) {
+ putUncheckedLong(v & 0xFFFF);
+ }
+ }
+
+ public void putInt(int v) {
+ if (isValidForSize(Integer.BYTES + 1)) {
+ putUncheckedLong(v & 0x00000000ffffffffL);
+ }
+ }
+
+ private void putUncheckedInt(int v) {
+ putUncheckedLong(v & 0x00000000ffffffffL);
+ }
+
+ public void putFloat(float i) {
+ if (isValidForSize(Float.BYTES)) {
+ currentPosition += Bits.putFloat(currentPosition, i);
+ }
+ }
+
+ public void putLong(long v) {
+ if (isValidForSize(Long.BYTES + 1)) {
+ putUncheckedLong(v);
+ }
+ }
+
+ public void putDouble(double i) {
+ if (isValidForSize(Double.BYTES)) {
+ currentPosition += Bits.putDouble(currentPosition, i);
+ }
+ }
+
+ public void putString(String s, StringPool pool) {
+ if (s == null) {
+ putByte(RecordingInput.STRING_ENCODING_NULL);
+ return;
+ }
+ int length = s.length();
+ if (length == 0) {
+ putByte(RecordingInput.STRING_ENCODING_EMPTY_STRING);
+ return;
+ }
+ if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) {
+ long l = StringPool.addString(s);
+ if (l > 0) {
+ putByte(RecordingInput.STRING_ENCODING_CONSTANT_POOL);
+ putLong(l);
+ return;
+ }
+ }
+ putStringValue(s);
+ return;
+ }
+
+ private void putStringValue(String s) {
+ int length = s.length();
+ if (isValidForSize(1 + 5 + 3 * length)) {
+ putUncheckedByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // 1 byte
+ putUncheckedInt(length); // max 5 bytes
+ for (int i = 0; i < length; i++) {
+ putUncheckedChar(s.charAt(i)); // max 3 bytes
+ }
+ }
+ }
+
+ public void putEventThread() {
+ putLong(threadID);
+ }
+
+ public void putThread(Thread athread) {
+ if (athread == null) {
+ putLong(0L);
+ } else {
+ putLong(jvm.getThreadId(athread));
+ }
+ }
+
+ public void putClass(Class<?> aClass) {
+ if (aClass == null) {
+ putLong(0L);
+ } else {
+ putLong(JVM.getClassIdNonIntrinsic(aClass));
+ }
+ }
+
+ public void putStackTrace() {
+ if (eventType.getStackTraceEnabled()) {
+ putLong(jvm.getStackTraceId(eventType.getStackTraceOffset()));
+ } else {
+ putLong(0L);
+ }
+ }
+
+ private void reserveEventSizeField() {
+ // move currentPosition Integer.Bytes offset from start position
+ if (isValidForSize(Integer.BYTES)) {
+ currentPosition += Integer.BYTES;
+ }
+ }
+
+ private void reset() {
+ currentPosition = startPosition;
+ if (flushOnEnd) {
+ flushOnEnd = flush();
+ }
+ valid = true;
+ started = false;
+ }
+
+ private boolean isValidForSize(int requestedSize) {
+ if (!valid) {
+ return false;
+ }
+ if (currentPosition + requestedSize > maxPosition) {
+ flushOnEnd = flush(usedSize(), requestedSize);
+ // retry
+ if (currentPosition + requestedSize > maxPosition) {
+ Logger.log(LogTag.JFR_SYSTEM,
+ LogLevel.WARN, () ->
+ "Unable to commit. Requested size " + requestedSize + " too large");
+ valid = false;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isNotified() {
+ return notified;
+ }
+
+ private void resetNotified() {
+ notified = false;
+ }
+
+ private int usedSize() {
+ return (int) (currentPosition - startPosition);
+ }
+
+ private boolean flush() {
+ return flush(usedSize(), 0);
+ }
+
+ private boolean flush(int usedSize, int requestedSize) {
+ return JVM.flush(this, usedSize, requestedSize);
+ }
+
+ public boolean beginEvent(PlatformEventType eventType) {
+ if (started) {
+ // recursive write attempt
+ return false;
+ }
+ started = true;
+ this.eventType = eventType;
+ reserveEventSizeField();
+ putLong(eventType.getId());
+ return true;
+ }
+
+ public boolean endEvent() {
+ if (!valid) {
+ reset();
+ return true;
+ }
+ final int eventSize = usedSize();
+ if (eventSize > maxEventSize) {
+ reset();
+ return true;
+ }
+ Bits.putInt(startPosition, makePaddedInt(eventSize));
+ if (isNotified()) {
+ resetNotified();
+ reset();
+ // returning false will trigger restart of the event write attempt
+ return false;
+ }
+ startPosition = currentPosition;
+ unsafe.putAddress(startPositionAddress, startPosition);
+ // the event is now committed
+ if (flushOnEnd) {
+ flushOnEnd = flush();
+ }
+ started = false;
+ return true;
+ }
+
+ private EventWriter(long startPos, long maxPos, long startPosAddress, long threadID, boolean valid) {
+ startPosition = currentPosition = startPos;
+ maxPosition = maxPos;
+ startPositionAddress = startPosAddress;
+ this.threadID = threadID;
+ started = false;
+ flushOnEnd = false;
+ this.valid = valid;
+ notified = false;
+ // event may not exceed size for a padded integer
+ maxEventSize = (1 << 28) -1;
+ }
+
+ private static int makePaddedInt(int v) {
+ // bit 0-6 + pad => bit 24 - 31
+ long b1 = (((v >>> 0) & 0x7F) | 0x80) << 24;
+
+ // bit 7-13 + pad => bit 16 - 23
+ long b2 = (((v >>> 7) & 0x7F) | 0x80) << 16;
+
+ // bit 14-20 + pad => bit 8 - 15
+ long b3 = (((v >>> 14) & 0x7F) | 0x80) << 8;
+
+ // bit 21-28 => bit 0 - 7
+ long b4 = (((v >>> 21) & 0x7F)) << 0;
+
+ return (int) (b1 + b2 + b3 + b4);
+ }
+
+ private void putUncheckedLong(long v) {
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 0-6
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 0-6
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 7-13
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 7-13
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 14-20
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 14-20
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 21-27
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 21-27
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 28-34
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 28-34
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 35-41
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 35-41
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 42-48
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 42-48
+ v >>>= 7;
+
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 49-55
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 49-55
+ putUncheckedByte((byte) (v >>> 7)); // 56-63, last byte as is.
+ }
+
+ private void putUncheckedByte(byte i) {
+ unsafe.putByte(currentPosition, i);
+ ++currentPosition;
+ }
+}