1 /*
  2  * Copyright (c) 2016, 2021, 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 static final 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 
 58     public static EventWriter getEventWriter() {
 59         EventWriter ew = JVM.getEventWriter();
 60         return ew != null ? ew : JVM.newEventWriter();
 61     }
 62 
 63     public void putBoolean(boolean i) {
 64         if (isValidForSize(Byte.BYTES)) {
 65             currentPosition += Bits.putBoolean(currentPosition, i);
 66         }
 67     }
 68 
 69     public void putByte(byte i) {
 70         if (isValidForSize(Byte.BYTES)) {
 71             unsafe.putByte(currentPosition, i);
 72             ++currentPosition;
 73         }
 74     }
 75 
 76     public void putChar(char v) {
 77         if (isValidForSize(Character.BYTES + 1)) {
 78             putUncheckedLong(v);
 79         }
 80     }
 81 
 82     private void putUncheckedChar(char v) {
 83         putUncheckedLong(v);
 84     }
 85 
 86     public void putShort(short v) {
 87         if (isValidForSize(Short.BYTES + 1)) {
 88             putUncheckedLong(v & 0xFFFF);
 89         }
 90     }
 91 
 92     public void putInt(int v) {
 93         if (isValidForSize(Integer.BYTES + 1)) {
 94             putUncheckedLong(v & 0x00000000ffffffffL);
 95         }
 96     }
 97 
 98     private void putUncheckedInt(int v) {
 99         putUncheckedLong(v & 0x00000000ffffffffL);
100     }
101 
102     public void putFloat(float i) {
103         if (isValidForSize(Float.BYTES)) {
104             currentPosition += Bits.putFloat(currentPosition, i);
105         }
106     }
107 
108     public void putLong(long v) {
109         if (isValidForSize(Long.BYTES + 1)) {
110             putUncheckedLong(v);
111         }
112     }
113 
114     public void putDouble(double i) {
115         if (isValidForSize(Double.BYTES)) {
116             currentPosition += Bits.putDouble(currentPosition, i);
117         }
118     }
119 
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 
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 
153     public void putEventThread() {
154         putLong(threadID);
155     }
156 
157     public void putThread(Thread athread) {
158         if (athread == null) {
159             putLong(0L);
160         } else {
161             putLong(jvm.getThreadId(athread));
162         }
163     }
164 
165     public void putClass(Class<?> aClass) {
166         if (aClass == null) {
167             putLong(0L);
168         } else {
169             putLong(JVM.getClassId(aClass));
170         }
171     }
172 
173     public void putStackTrace() {
174         if (eventType.getStackTraceEnabled()) {
175             putLong(jvm.getStackTraceId(eventType.getStackTraceOffset()));
176         } else {
177             putLong(0L);
178         }
179     }
180 
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 
194     public void reset() {
195         currentPosition = startPosition;
196         if (flushOnEnd) {
197             flushOnEnd = flush();
198         }
199         valid = true;
200         started = false;
201     }
202 
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 
217     private boolean isNotified() {
218         return notified;
219     }
220 
221     private void resetNotified() {
222         notified = false;
223     }
224 
225     private void resetStringPool() {
226         StringPool.reset();
227     }
228 
229     private int usedSize() {
230         return (int) (currentPosition - startPosition);
231     }
232 
233     private boolean flush() {
234         return flush(usedSize(), 0);
235     }
236 
237     private boolean flush(int usedSize, int requestedSize) {
238         return JVM.flush(this, usedSize, requestedSize);
239     }
240 
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 
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 
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 
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 
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 
375     private void putUncheckedByte(byte i) {
376         unsafe.putByte(currentPosition, i);
377         ++currentPosition;
378     }
379 }