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 jdk.internal.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 }