1 /*
   2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jfr.internal;
  27 
  28 import sun.misc.Unsafe;
  29 import jdk.jfr.internal.consumer.RecordingInput;
  30 
  31 /**
  32  * Class must reside in a package with package restriction.
  33  *
  34  * Users should not have direct access to underlying memory.
  35  *
  36  */
  37 public final class EventWriter {
  38     private static final Unsafe unsafe = Unsafe.getUnsafe();
  39     private final static JVM jvm = JVM.getJVM();
  40 
  41     private long startPosition;
  42     private long startPositionAddress;
  43     private long currentPosition;
  44     private long maxPosition;
  45     private final long threadID;
  46     private PlatformEventType eventType;
  47     private int maxEventSize;
  48     private boolean started;
  49     private boolean valid;
  50     private boolean flushOnEnd;
  51     // set by the JVM, not private to avoid being optimized out
  52     boolean notified;
  53 
  54     public static EventWriter getEventWriter() {
  55         EventWriter ew = (EventWriter)JVM.getEventWriter();
  56         return ew != null ? ew : JVM.newEventWriter();
  57     }
  58 
  59     public void putBoolean(boolean i) {
  60         if (isValidForSize(Byte.BYTES)) {
  61             currentPosition += Bits.putBoolean(currentPosition, i);
  62         }
  63     }
  64 
  65     public void putByte(byte i) {
  66         if (isValidForSize(Byte.BYTES)) {
  67             unsafe.putByte(currentPosition, i);
  68             ++currentPosition;
  69         }
  70     }
  71 
  72     public void putChar(char v) {
  73         if (isValidForSize(Character.BYTES + 1)) {
  74             putUncheckedLong(v);
  75         }
  76     }
  77 
  78     private void putUncheckedChar(char v) {
  79         putUncheckedLong(v);
  80     }
  81 
  82     public void putShort(short v) {
  83         if (isValidForSize(Short.BYTES + 1)) {
  84             putUncheckedLong(v & 0xFFFF);
  85         }
  86     }
  87 
  88     public void putInt(int v) {
  89         if (isValidForSize(Integer.BYTES + 1)) {
  90             putUncheckedLong(v & 0x00000000ffffffffL);
  91         }
  92     }
  93 
  94     private void putUncheckedInt(int v) {
  95         putUncheckedLong(v & 0x00000000ffffffffL);
  96     }
  97 
  98     public void putFloat(float i) {
  99         if (isValidForSize(Float.BYTES)) {
 100             currentPosition += Bits.putFloat(currentPosition, i);
 101         }
 102     }
 103 
 104     public void putLong(long v) {
 105         if (isValidForSize(Long.BYTES + 1)) {
 106             putUncheckedLong(v);
 107         }
 108     }
 109 
 110     public void putDouble(double i) {
 111         if (isValidForSize(Double.BYTES)) {
 112             currentPosition += Bits.putDouble(currentPosition, i);
 113         }
 114     }
 115 
 116     public void putString(String s, StringPool pool) {
 117         if (s == null) {
 118             putByte(RecordingInput.STRING_ENCODING_NULL);
 119             return;
 120         }
 121         int length = s.length();
 122         if (length == 0) {
 123             putByte(RecordingInput.STRING_ENCODING_EMPTY_STRING);
 124             return;
 125         }
 126         if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) {
 127             long l = StringPool.addString(s);
 128             if (l > 0) {
 129                 putByte(RecordingInput.STRING_ENCODING_CONSTANT_POOL);
 130                 putLong(l);
 131                 return;
 132             }
 133         }
 134         putStringValue(s);
 135         return;
 136     }
 137 
 138     private void putStringValue(String s) {
 139         int length = s.length();
 140         if (isValidForSize(1 + 5 + 3 * length)) {
 141             putUncheckedByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // 1 byte
 142             putUncheckedInt(length); // max 5 bytes
 143             for (int i = 0; i < length; i++) {
 144                 putUncheckedChar(s.charAt(i)); // max 3 bytes
 145             }
 146         }
 147     }
 148 
 149     public void putEventThread() {
 150         putLong(threadID);
 151     }
 152 
 153     public void putThread(Thread athread) {
 154         if (athread == null) {
 155             putLong(0L);
 156         } else {
 157             putLong(jvm.getThreadId(athread));
 158         }
 159     }
 160 
 161     public void putClass(Class<?> aClass) {
 162         if (aClass == null) {
 163             putLong(0L);
 164         } else {
 165             putLong(JVM.getClassIdNonIntrinsic(aClass));
 166         }
 167     }
 168 
 169     public void putStackTrace() {
 170         if (eventType.getStackTraceEnabled()) {
 171             putLong(jvm.getStackTraceId(eventType.getStackTraceOffset()));
 172         } else {
 173             putLong(0L);
 174         }
 175     }
 176 
 177     private void reserveEventSizeField() {
 178         // move currentPosition Integer.Bytes offset from start position
 179         if (isValidForSize(Integer.BYTES)) {
 180             currentPosition += Integer.BYTES;
 181         }
 182     }
 183 
 184     private void reset() {
 185         currentPosition = startPosition;
 186         if (flushOnEnd) {
 187             flushOnEnd = flush();
 188         }
 189         valid = true;
 190         started = false;
 191     }
 192 
 193     private boolean isValidForSize(int requestedSize) {
 194         if (!valid) {
 195             return false;
 196         }
 197         if (currentPosition + requestedSize > maxPosition) {
 198             flushOnEnd = flush(usedSize(), requestedSize);
 199             // retry
 200             if (currentPosition + requestedSize > maxPosition) {
 201                 Logger.log(LogTag.JFR_SYSTEM,
 202                            LogLevel.WARN, () ->
 203                                "Unable to commit. Requested size " + requestedSize + " too large");
 204                 valid = false;
 205                 return false;
 206             }
 207         }
 208         return true;
 209     }
 210 
 211     private boolean isNotified() {
 212         return notified;
 213     }
 214 
 215     private void resetNotified() {
 216         notified = false;
 217     }
 218 
 219     private int usedSize() {
 220         return (int) (currentPosition - startPosition);
 221     }
 222 
 223     private boolean flush() {
 224         return flush(usedSize(), 0);
 225     }
 226 
 227     private boolean flush(int usedSize, int requestedSize) {
 228         return JVM.flush(this, usedSize, requestedSize);
 229     }
 230 
 231     public boolean beginEvent(PlatformEventType eventType) {
 232         if (started) {
 233             // recursive write attempt
 234             return false;
 235         }
 236         started = true;
 237         this.eventType = eventType;
 238         reserveEventSizeField();
 239         putLong(eventType.getId());
 240         return true;
 241     }
 242 
 243     public boolean endEvent() {
 244         if (!valid) {
 245             reset();
 246             return true;
 247         }
 248         final int eventSize = usedSize();
 249         if (eventSize > maxEventSize) {
 250             reset();
 251             return true;
 252         }
 253         Bits.putInt(startPosition, makePaddedInt(eventSize));
 254         if (isNotified()) {
 255             resetNotified();
 256             reset();
 257             // returning false will trigger restart of the event write attempt
 258             return false;
 259         }
 260         startPosition = currentPosition;
 261         unsafe.putAddress(startPositionAddress, startPosition);
 262         // the event is now committed
 263         if (flushOnEnd) {
 264             flushOnEnd = flush();
 265         }
 266         started = false;
 267         return true;
 268     }
 269 
 270     private EventWriter(long startPos, long maxPos, long startPosAddress, long threadID, boolean valid) {
 271         startPosition = currentPosition = startPos;
 272         maxPosition = maxPos;
 273         startPositionAddress = startPosAddress;
 274         this.threadID = threadID;
 275         started = false;
 276         flushOnEnd = false;
 277         this.valid = valid;
 278         notified = false;
 279         // event may not exceed size for a padded integer
 280         maxEventSize = (1 << 28) -1;
 281     }
 282 
 283     private static int makePaddedInt(int v) {
 284         // bit  0-6 + pad => bit 24 - 31
 285         long b1 = (((v >>> 0) & 0x7F) | 0x80) << 24;
 286 
 287         // bit  7-13 + pad => bit 16 - 23
 288         long b2 = (((v >>> 7) & 0x7F) | 0x80) << 16;
 289 
 290         // bit 14-20 + pad => bit  8 - 15
 291         long b3 = (((v >>> 14) & 0x7F) | 0x80) << 8;
 292 
 293         // bit 21-28       => bit  0 -  7
 294         long b4 = (((v >>> 21) & 0x7F)) << 0;
 295 
 296         return (int) (b1 + b2 + b3 + b4);
 297     }
 298 
 299     private void putUncheckedLong(long v) {
 300         if ((v & ~0x7FL) == 0L) {
 301             putUncheckedByte((byte) v); // 0-6
 302             return;
 303         }
 304         putUncheckedByte((byte) (v | 0x80L)); // 0-6
 305         v >>>= 7;
 306         if ((v & ~0x7FL) == 0L) {
 307             putUncheckedByte((byte) v); // 7-13
 308             return;
 309         }
 310         putUncheckedByte((byte) (v | 0x80L)); // 7-13
 311         v >>>= 7;
 312         if ((v & ~0x7FL) == 0L) {
 313             putUncheckedByte((byte) v); // 14-20
 314             return;
 315         }
 316         putUncheckedByte((byte) (v | 0x80L)); // 14-20
 317         v >>>= 7;
 318         if ((v & ~0x7FL) == 0L) {
 319             putUncheckedByte((byte) v); // 21-27
 320             return;
 321         }
 322         putUncheckedByte((byte) (v | 0x80L)); // 21-27
 323         v >>>= 7;
 324         if ((v & ~0x7FL) == 0L) {
 325             putUncheckedByte((byte) v); // 28-34
 326             return;
 327         }
 328         putUncheckedByte((byte) (v | 0x80L)); // 28-34
 329         v >>>= 7;
 330         if ((v & ~0x7FL) == 0L) {
 331             putUncheckedByte((byte) v); // 35-41
 332             return;
 333         }
 334         putUncheckedByte((byte) (v | 0x80L)); // 35-41
 335         v >>>= 7;
 336         if ((v & ~0x7FL) == 0L) {
 337             putUncheckedByte((byte) v); // 42-48
 338             return;
 339         }
 340         putUncheckedByte((byte) (v | 0x80L)); // 42-48
 341         v >>>= 7;
 342 
 343         if ((v & ~0x7FL) == 0L) {
 344             putUncheckedByte((byte) v); // 49-55
 345             return;
 346         }
 347         putUncheckedByte((byte) (v | 0x80L)); // 49-55
 348         putUncheckedByte((byte) (v >>> 7)); // 56-63, last byte as is.
 349     }
 350 
 351     private void putUncheckedByte(byte i) {
 352         unsafe.putByte(currentPosition, i);
 353         ++currentPosition;
 354     }
 355 }