|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.jfr.internal; |
|
|
|
import sun.misc.Unsafe; |
|
import jdk.jfr.internal.consumer.RecordingInput; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
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); |
|
putUncheckedInt(length); |
|
for (int i = 0; i < length; i++) { |
|
putUncheckedChar(s.charAt(i)); |
|
} |
|
} |
|
} |
|
|
|
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() { |
|
|
|
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); |
|
|
|
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) { |
|
|
|
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(); |
|
|
|
return false; |
|
} |
|
startPosition = currentPosition; |
|
unsafe.putAddress(startPositionAddress, startPosition); |
|
|
|
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; |
|
|
|
maxEventSize = (1 << 28) -1; |
|
} |
|
|
|
private static int makePaddedInt(int v) { |
|
|
|
long b1 = (((v >>> 0) & 0x7F) | 0x80) << 24; |
|
|
|
|
|
long b2 = (((v >>> 7) & 0x7F) | 0x80) << 16; |
|
|
|
|
|
long b3 = (((v >>> 14) & 0x7F) | 0x80) << 8; |
|
|
|
|
|
long b4 = (((v >>> 21) & 0x7F)) << 0; |
|
|
|
return (int) (b1 + b2 + b3 + b4); |
|
} |
|
|
|
private void putUncheckedLong(long v) { |
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
v >>>= 7; |
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
v >>>= 7; |
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
v >>>= 7; |
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
v >>>= 7; |
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
v >>>= 7; |
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
v >>>= 7; |
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
v >>>= 7; |
|
|
|
if ((v & ~0x7FL) == 0L) { |
|
putUncheckedByte((byte) v); |
|
return; |
|
} |
|
putUncheckedByte((byte) (v | 0x80L)); |
|
putUncheckedByte((byte) (v >>> 7)); |
|
} |
|
|
|
private void putUncheckedByte(byte i) { |
|
unsafe.putByte(currentPosition, i); |
|
++currentPosition; |
|
} |
|
} |