1 /* 2 * Copyright (c) 2016, 2020, 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 jdk.internal.misc.Unsafe; 29 import jdk.jfr.internal.consumer.StringParser; 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 39 // Event may not exceed size for a padded integer 40 private static final long MAX_EVENT_SIZE = (1 << 28) -1; 41 private static final Unsafe unsafe = Unsafe.getUnsafe(); 42 private final static JVM jvm = JVM.getJVM(); 43 44 // The JVM needs access to these values. Don't remove 45 private final long threadID; 46 private long startPosition; 47 private long startPositionAddress; 48 private long currentPosition; 49 private long maxPosition; 50 private boolean valid; 51 boolean notified; // Not private to avoid being optimized away 52 53 private PlatformEventType eventType; 54 private boolean started; 55 private boolean flushOnEnd; 56 private boolean largeSize = false; 57 getEventWriter()58 public static EventWriter getEventWriter() { 59 EventWriter ew = (EventWriter)JVM.getEventWriter(); 60 return ew != null ? ew : JVM.newEventWriter(); 61 } 62 putBoolean(boolean i)63 public void putBoolean(boolean i) { 64 if (isValidForSize(Byte.BYTES)) { 65 currentPosition += Bits.putBoolean(currentPosition, i); 66 } 67 } 68 putByte(byte i)69 public void putByte(byte i) { 70 if (isValidForSize(Byte.BYTES)) { 71 unsafe.putByte(currentPosition, i); 72 ++currentPosition; 73 } 74 } 75 putChar(char v)76 public void putChar(char v) { 77 if (isValidForSize(Character.BYTES + 1)) { 78 putUncheckedLong(v); 79 } 80 } 81 putUncheckedChar(char v)82 private void putUncheckedChar(char v) { 83 putUncheckedLong(v); 84 } 85 putShort(short v)86 public void putShort(short v) { 87 if (isValidForSize(Short.BYTES + 1)) { 88 putUncheckedLong(v & 0xFFFF); 89 } 90 } 91 putInt(int v)92 public void putInt(int v) { 93 if (isValidForSize(Integer.BYTES + 1)) { 94 putUncheckedLong(v & 0x00000000ffffffffL); 95 } 96 } 97 putUncheckedInt(int v)98 private void putUncheckedInt(int v) { 99 putUncheckedLong(v & 0x00000000ffffffffL); 100 } 101 putFloat(float i)102 public void putFloat(float i) { 103 if (isValidForSize(Float.BYTES)) { 104 currentPosition += Bits.putFloat(currentPosition, i); 105 } 106 } 107 putLong(long v)108 public void putLong(long v) { 109 if (isValidForSize(Long.BYTES + 1)) { 110 putUncheckedLong(v); 111 } 112 } 113 putDouble(double i)114 public void putDouble(double i) { 115 if (isValidForSize(Double.BYTES)) { 116 currentPosition += Bits.putDouble(currentPosition, i); 117 } 118 } 119 putString(String s, StringPool pool)120 public void putString(String s, StringPool pool) { 121 if (s == null) { 122 putByte(StringParser.Encoding.NULL.byteValue()); 123 return; 124 } 125 int length = s.length(); 126 if (length == 0) { 127 putByte(StringParser.Encoding.EMPTY_STRING.byteValue()); 128 return; 129 } 130 if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) { 131 long l = StringPool.addString(s); 132 if (l > 0) { 133 putByte(StringParser.Encoding.CONSTANT_POOL.byteValue()); 134 putLong(l); 135 return; 136 } 137 } 138 putStringValue(s); 139 return; 140 } 141 putStringValue(String s)142 private void putStringValue(String s) { 143 int length = s.length(); 144 if (isValidForSize(1 + 5 + 3 * length)) { 145 putUncheckedByte(StringParser.Encoding.CHAR_ARRAY.byteValue()); // 1 byte 146 putUncheckedInt(length); // max 5 bytes 147 for (int i = 0; i < length; i++) { 148 putUncheckedChar(s.charAt(i)); // max 3 bytes 149 } 150 } 151 } 152 putEventThread()153 public void putEventThread() { 154 putLong(threadID); 155 } 156 putThread(Thread athread)157 public void putThread(Thread athread) { 158 if (athread == null) { 159 putLong(0L); 160 } else { 161 putLong(jvm.getThreadId(athread)); 162 } 163 } 164 putClass(Class<?> aClass)165 public void putClass(Class<?> aClass) { 166 if (aClass == null) { 167 putLong(0L); 168 } else { 169 putLong(JVM.getClassIdNonIntrinsic(aClass)); 170 } 171 } 172 putStackTrace()173 public void putStackTrace() { 174 if (eventType.getStackTraceEnabled()) { 175 putLong(jvm.getStackTraceId(eventType.getStackTraceOffset())); 176 } else { 177 putLong(0L); 178 } 179 } 180 reserveEventSizeField()181 private void reserveEventSizeField() { 182 this.largeSize = eventType.isLargeSize(); 183 if (largeSize) { 184 if (isValidForSize(Integer.BYTES)) { 185 currentPosition += Integer.BYTES; 186 } 187 } else { 188 if (isValidForSize(1)) { 189 currentPosition += 1; 190 } 191 } 192 } 193 reset()194 private void reset() { 195 currentPosition = startPosition; 196 if (flushOnEnd) { 197 flushOnEnd = flush(); 198 } 199 valid = true; 200 started = false; 201 } 202 isValidForSize(int requestedSize)203 private boolean isValidForSize(int requestedSize) { 204 if (!valid) { 205 return false; 206 } 207 if (currentPosition + requestedSize > maxPosition) { 208 flushOnEnd = flush(usedSize(), requestedSize); 209 // retry 210 if (!valid) { 211 return false; 212 } 213 } 214 return true; 215 } 216 isNotified()217 private boolean isNotified() { 218 return notified; 219 } 220 resetNotified()221 private void resetNotified() { 222 notified = false; 223 } 224 resetStringPool()225 private void resetStringPool() { 226 StringPool.reset(); 227 } 228 usedSize()229 private int usedSize() { 230 return (int) (currentPosition - startPosition); 231 } 232 flush()233 private boolean flush() { 234 return flush(usedSize(), 0); 235 } 236 flush(int usedSize, int requestedSize)237 private boolean flush(int usedSize, int requestedSize) { 238 return JVM.flush(this, usedSize, requestedSize); 239 } 240 beginEvent(PlatformEventType eventType)241 public boolean beginEvent(PlatformEventType eventType) { 242 if (started) { 243 // recursive write attempt 244 return false; 245 } 246 started = true; 247 this.eventType = eventType; 248 reserveEventSizeField(); 249 putLong(eventType.getId()); 250 return true; 251 } 252 endEvent()253 public boolean endEvent() { 254 if (!valid) { 255 reset(); 256 return true; 257 } 258 final int eventSize = usedSize(); 259 if (eventSize > MAX_EVENT_SIZE) { 260 reset(); 261 return true; 262 } 263 264 if (largeSize) { 265 Bits.putInt(startPosition, makePaddedInt(eventSize)); 266 } else { 267 if (eventSize < 128) { 268 Bits.putByte(startPosition, (byte) eventSize); 269 } else { 270 eventType.setLargeSize(); 271 reset(); 272 // returning false will trigger restart of the 273 // event write attempt 274 return false; 275 } 276 } 277 278 if (isNotified()) { 279 resetNotified(); 280 resetStringPool(); 281 reset(); 282 // returning false will trigger restart of the event write attempt 283 return false; 284 } 285 startPosition = currentPosition; 286 unsafe.storeStoreFence(); 287 unsafe.putAddress(startPositionAddress, currentPosition); 288 // the event is now committed 289 if (flushOnEnd) { 290 flushOnEnd = flush(); 291 } 292 started = false; 293 return true; 294 } 295 EventWriter(long startPos, long maxPos, long startPosAddress, long threadID, boolean valid)296 private EventWriter(long startPos, long maxPos, long startPosAddress, long threadID, boolean valid) { 297 startPosition = currentPosition = startPos; 298 maxPosition = maxPos; 299 startPositionAddress = startPosAddress; 300 this.threadID = threadID; 301 started = false; 302 flushOnEnd = false; 303 this.valid = valid; 304 notified = false; 305 } 306 makePaddedInt(int v)307 private static int makePaddedInt(int v) { 308 // bit 0-6 + pad => bit 24 - 31 309 long b1 = (((v >>> 0) & 0x7F) | 0x80) << 24; 310 311 // bit 7-13 + pad => bit 16 - 23 312 long b2 = (((v >>> 7) & 0x7F) | 0x80) << 16; 313 314 // bit 14-20 + pad => bit 8 - 15 315 long b3 = (((v >>> 14) & 0x7F) | 0x80) << 8; 316 317 // bit 21-28 => bit 0 - 7 318 long b4 = (((v >>> 21) & 0x7F)) << 0; 319 320 return (int) (b1 + b2 + b3 + b4); 321 } 322 putUncheckedLong(long v)323 private void putUncheckedLong(long v) { 324 if ((v & ~0x7FL) == 0L) { 325 putUncheckedByte((byte) v); // 0-6 326 return; 327 } 328 putUncheckedByte((byte) (v | 0x80L)); // 0-6 329 v >>>= 7; 330 if ((v & ~0x7FL) == 0L) { 331 putUncheckedByte((byte) v); // 7-13 332 return; 333 } 334 putUncheckedByte((byte) (v | 0x80L)); // 7-13 335 v >>>= 7; 336 if ((v & ~0x7FL) == 0L) { 337 putUncheckedByte((byte) v); // 14-20 338 return; 339 } 340 putUncheckedByte((byte) (v | 0x80L)); // 14-20 341 v >>>= 7; 342 if ((v & ~0x7FL) == 0L) { 343 putUncheckedByte((byte) v); // 21-27 344 return; 345 } 346 putUncheckedByte((byte) (v | 0x80L)); // 21-27 347 v >>>= 7; 348 if ((v & ~0x7FL) == 0L) { 349 putUncheckedByte((byte) v); // 28-34 350 return; 351 } 352 putUncheckedByte((byte) (v | 0x80L)); // 28-34 353 v >>>= 7; 354 if ((v & ~0x7FL) == 0L) { 355 putUncheckedByte((byte) v); // 35-41 356 return; 357 } 358 putUncheckedByte((byte) (v | 0x80L)); // 35-41 359 v >>>= 7; 360 if ((v & ~0x7FL) == 0L) { 361 putUncheckedByte((byte) v); // 42-48 362 return; 363 } 364 putUncheckedByte((byte) (v | 0x80L)); // 42-48 365 v >>>= 7; 366 367 if ((v & ~0x7FL) == 0L) { 368 putUncheckedByte((byte) v); // 49-55 369 return; 370 } 371 putUncheckedByte((byte) (v | 0x80L)); // 49-55 372 putUncheckedByte((byte) (v >>> 7)); // 56-63, last byte as is. 373 } 374 putUncheckedByte(byte i)375 private void putUncheckedByte(byte i) { 376 unsafe.putByte(currentPosition, i); 377 ++currentPosition; 378 } 379 } 380