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