descriptors, long id, Object[] values, TimeConverter timeConverter) {
+ super(descriptors, values, timeConverter);
+ this.uniqueId = id;
+ }
+
+ /**
+ * Returns the modifiers of the class.
+ *
+ * See {@link java.lang.reflect.Modifier}
+ *
+ * @return the modifiers
+ *
+ * @see Modifier
+ */
+ public int getModifiers() {
+ return getTyped("modifiers", Integer.class, -1);
+ }
+
+ /**
+ * Returns the class loader that defined the class.
+ *
+ * If the bootstrap class loader is represented as {@code null} in the Java
+ * Virtual Machine (JVM), then {@code null} is also the return value of this method.
+ *
+ * @return the class loader defining this class, can be {@code null}
+ */
+ public RecordedClassLoader getClassLoader() {
+ return getTyped("classLoader", RecordedClassLoader.class, null);
+ }
+
+ /**
+ * Returns the fully qualified name of the class (for example,
+ * {@code "java.lang.String"}).
+ *
+ * @return the class name, not {@code null}
+ */
+ public String getName() {
+ return getTyped("name", String.class, null).replace("/", ".");
+ }
+
+ /**
+ * Returns a unique ID for the class.
+ *
+ * The ID might not be the same between Java Virtual Machine (JVM) instances.
+ *
+ * @return a unique ID
+ */
+ public long getId() {
+ return uniqueId;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedClassLoader.java b/src/share/classes/jdk/jfr/consumer/RecordedClassLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ff6754b80673fd2d91c128e04fc6e6055bcdde9
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedClassLoader.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.util.List;
+
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.Type;
+
+/**
+ * A recorded Java class loader.
+ *
+ * @since 9
+ */
+public final class RecordedClassLoader extends RecordedObject {
+
+ static ObjectFactory createFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory(type) {
+ @Override
+ RecordedClassLoader createTyped(List desc, long id, Object[] object) {
+ return new RecordedClassLoader(desc, id, object, timeConverter);
+ }
+ };
+ }
+
+ private final long uniqueId;
+
+ // package private
+ private RecordedClassLoader(List descriptors, long id, Object[] values, TimeConverter timeConverter) {
+ super(descriptors, values, timeConverter);
+ this.uniqueId = id;
+ }
+
+ /**
+ * Returns the class of the class loader.
+ *
+ * If the bootstrap class loader is represented as {@code null} in the Java
+ * Virtual Machine (JVM), then {@code null} is also the return value of this
+ * method.
+ *
+ * @return class of the class loader, can be {@code null}
+ */
+ public RecordedClass getType() {
+ return getTyped("type", RecordedClass.class, null);
+ }
+
+ /**
+ * Returns the name of the class loader (for example, "boot", "platform", and
+ * "app").
+ *
+ * @return the class loader name, can be {@code null}
+ */
+ public String getName() {
+ return getTyped("name", String.class, null);
+ }
+
+ /**
+ * Returns a unique ID for the class loader.
+ *
+ * The ID might not be the same between Java Virtual Machine (JVM) instances.
+ *
+ * @return a unique ID
+ */
+ public long getId() {
+ return uniqueId;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedEvent.java b/src/share/classes/jdk/jfr/consumer/RecordedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbcda7afc9334c9dd6f5e43a99eff30038d7f9a7
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedEvent.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.EventInstrumentation;
+
+/**
+ * A recorded event.
+ *
+ * @since 9
+ */
+public final class RecordedEvent extends RecordedObject {
+ private final EventType eventType;
+ private final long startTime;
+ private final long endTime;
+
+ // package private
+ RecordedEvent(EventType type, List vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
+ super(vds, values, timeConverter);
+ this.eventType = type;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ /**
+ * Returns the stack trace that was created when the event was committed, or
+ * {@code null} if the event lacks a stack trace.
+ *
+ * @return stack trace, or {@code null} if doesn't exist for the event
+ */
+ public RecordedStackTrace getStackTrace() {
+ return getTyped(EventInstrumentation.FIELD_STACK_TRACE, RecordedStackTrace.class, null);
+ }
+
+ /**
+ * Returns the thread from which the event was committed, or {@code null} if
+ * the thread was not recorded.
+ *
+ * @return thread, or {@code null} if doesn't exist for the event
+ */
+ public RecordedThread getThread() {
+ return getTyped(EventInstrumentation.FIELD_EVENT_THREAD, RecordedThread.class, null);
+ }
+
+ /**
+ * Returns the event type that describes the event.
+ *
+ * @return the event type, not {@code null}
+ */
+ public EventType getEventType() {
+ return eventType;
+ }
+
+ /**
+ * Returns the start time of the event.
+ *
+ * If the event is an instant event, then the start time and end time are the same.
+ *
+ * @return the start time, not {@code null}
+ */
+ public Instant getStartTime() {
+ return Instant.ofEpochSecond(0, startTime);
+ }
+
+ /**
+ * Returns the end time of the event.
+ *
+ * If the event is an instant event, then the start time and end time are the same.
+ *
+ * @return the end time, not {@code null}
+ */
+ public Instant getEndTime() {
+ return Instant.ofEpochSecond(0, endTime);
+ }
+
+ /**
+ * Returns the duration of the event, measured in nanoseconds.
+ *
+ * @return the duration in nanoseconds, not {@code null}
+ */
+ public Duration getDuration() {
+ return Duration.ofNanos(endTime - startTime);
+ }
+
+ /**
+ * Returns the list of descriptors that describes the fields of the event.
+ *
+ * @return descriptors, not {@code null}
+ */
+ @Override
+ public List getFields() {
+ return getEventType().getFields();
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedFrame.java b/src/share/classes/jdk/jfr/consumer/RecordedFrame.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0b9ccacdf7ae3461949f102b237608b32f6ca49
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedFrame.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.Type;
+
+/**
+ * A recorded frame in a stack trace.
+ *
+ * @since 9
+ */
+public final class RecordedFrame extends RecordedObject {
+
+ static ObjectFactory createFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory(type) {
+ @Override
+ RecordedFrame createTyped(List desc, long id, Object[] object) {
+ return new RecordedFrame(desc, object, timeConverter);
+ }
+ };
+ }
+
+ // package private
+ RecordedFrame(List desc, Object[] objects, TimeConverter timeConverter) {
+ super(desc, objects, timeConverter);
+ }
+
+ /**
+ * Returns {@code true} if this is a Java frame, {@code false} otherwise.
+ *
+ * A Java method that has a native modifier is considered a Java frame.
+ *
+ * @return {@code true} if this is a Java frame, {@code false} otherwise
+ *
+ * @see Modifier#isNative(int)
+ */
+ public boolean isJavaFrame() {
+ // Only Java frames exist today, but this allows
+ // API to be extended for native frame in the future.
+ if (hasField("javaFrame")) {
+ return getTyped("javaFrame", Boolean.class, Boolean.TRUE);
+ }
+ return true;
+ }
+
+ /**
+ * Returns the bytecode index for the execution point that is represented by
+ * this recorded frame.
+ *
+ * @return byte code index, or {@code -1} if doesn't exist
+ */
+ public int getBytecodeIndex() {
+ return getTyped("bytecodeIndex", Integer.class, Integer.valueOf(-1));
+ }
+
+ /**
+ * Returns the line number for the execution point that is represented by this
+ * recorded frame, or {@code -1} if doesn't exist
+ *
+ * @return the line number, or {@code -1} if doesn't exist
+ */
+ public int getLineNumber() {
+ return getTyped("lineNumber", Integer.class, Integer.valueOf(-1));
+ }
+
+ /**
+ * Returns the frame type for the execution point that is represented by this
+ * recorded frame (for example, {@code "Interpreted"}, {@code "JIT compiled"} or
+ * {@code "Inlined"}).
+ *
+ * @return the frame type, or {@code null} if doesn't exist
+ */
+ public String getType() {
+ return getTyped("type", String.class, null);
+ }
+
+ /**
+ * Returns the method for the execution point that is represented by this
+ * recorded frame.
+ *
+ * @return the method, not {@code null}
+ */
+ public RecordedMethod getMethod() {
+ return getTyped("method", RecordedMethod.class, null);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedMethod.java b/src/share/classes/jdk/jfr/consumer/RecordedMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd0d2fe8d4f305b10fe7be4d68c2118edace1772
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedMethod.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.Type;
+
+/**
+ * A recorded method.
+ *
+ * @since 9
+ */
+public final class RecordedMethod extends RecordedObject {
+
+ static ObjectFactory createFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory(type) {
+ @Override
+ RecordedMethod createTyped(List desc, long id, Object[] object) {
+ return new RecordedMethod(desc, object, timeConverter);
+ }
+ };
+ }
+
+ private RecordedMethod(List descriptors, Object[] objects, TimeConverter timeConverter) {
+ super(descriptors, objects, timeConverter);
+ }
+
+ /**
+ * Returns the class this method belongs to, if it belong to a Java frame.
+ *
+ * To ensure this is a Java frame, use the {@link RecordedFrame#isJavaFrame()}
+ * method.
+ *
+ * @return the class, may be {@code null} if not a Java frame
+ *
+ * @see RecordedFrame#isJavaFrame()
+ */
+ public RecordedClass getType() {
+ return getTyped("type", RecordedClass.class, null);
+ }
+
+ /**
+ * Returns the name of this method, for example {@code "toString"}.
+ *
+ * If this method doesn't belong to a Java frame the result is undefined.
+ *
+ * @return method name, or {@code null} if doesn't exist
+ *
+ * @see RecordedFrame#isJavaFrame()
+ */
+ public String getName() {
+ return getTyped("name", String.class, null);
+ }
+
+ /**
+ * Returns the method descriptor for this method (for example,
+ * {@code "(Ljava/lang/String;)V"}).
+ *
+ * See Java Virtual Machine Specification, 4.3
+ *
+ * If this method doesn't belong to a Java frame then the the result is undefined.
+ *
+ * @return method descriptor.
+ *
+ * @see RecordedFrame#isJavaFrame()
+ */
+ public String getDescriptor() {
+ return getTyped("descriptor", String.class, null);
+ }
+
+ /**
+ * Returns the modifiers for this method.
+ *
+ * If this method doesn't belong to a Java frame, then the result is undefined.
+ *
+ * @return the modifiers
+ *
+ * @see Modifier
+ * @see RecordedFrame#isJavaFrame
+ */
+ public int getModifiers() {
+ return getTyped("modifiers", Integer.class, Integer.valueOf(0));
+ }
+
+ /**
+ * Returns whether this method is hidden (for example, wrapper code in a lambda
+ * expressions).
+ *
+ * @return {@code true} if method is hidden, {@code false} otherwise
+ */
+ public boolean isHidden() {
+ return getTyped("hidden", Boolean.class, Boolean.FALSE);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedObject.java b/src/share/classes/jdk/jfr/consumer/RecordedObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..90d19d2fd96c8b2b4e9c5aadb42e1a7397f72acb
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedObject.java
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+
+import jdk.jfr.Timespan;
+import jdk.jfr.Timestamp;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.cmd.PrettyWriter;
+
+/**
+ * A complex data type that consists of one or more fields.
+ *
+ * This class provides methods to select and query nested objects by passing a
+ * dot {@code "."} delimited {@code String} object (for instance,
+ * {@code "aaa.bbb"}). A method evaluates a nested object from left to right,
+ * and if a part is {@code null}, it throws {@code NullPointerException}.
+ *
+ * @since 9
+ */
+public class RecordedObject {
+
+ private final static class UnsignedValue {
+ private final Object o;
+
+ UnsignedValue(Object o) {
+ this.o = o;
+ }
+
+ Object value() {
+ return o;
+ }
+ }
+
+ private final Object[] objects;
+ private final List descriptors;
+ private final TimeConverter timeConverter;
+
+ // package private, not to be subclassed outside this package
+ RecordedObject(List descriptors, Object[] objects, TimeConverter timeConverter) {
+ this.descriptors = descriptors;
+ this.objects = objects;
+ this.timeConverter = timeConverter;
+ }
+
+ // package private
+ final T getTyped(String name, Class clazz, T defaultValue) {
+ // Unnecessary to check field presence twice, but this
+ // will do for now.
+ if (!hasField(name)) {
+ return defaultValue;
+ }
+ T object = getValue(name);
+ if (object == null || object.getClass().isAssignableFrom(clazz)) {
+ return object;
+ } else {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns {@code true} if a field with the given name exists, {@code false}
+ * otherwise.
+ *
+ * @param name name of the field to get, not {@code null}
+ *
+ * @return {@code true} if the field exists, {@code false} otherwise.
+ *
+ * @see #getFields()
+ */
+ public boolean hasField(String name) {
+ Objects.requireNonNull(name);
+ for (ValueDescriptor v : descriptors) {
+ if (v.getName().equals(name)) {
+ return true;
+ }
+ }
+ int dotIndex = name.indexOf(".");
+ if (dotIndex > 0) {
+ String structName = name.substring(0, dotIndex);
+ for (ValueDescriptor v : descriptors) {
+ if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
+ RecordedObject child = getValue(structName);
+ if (child != null) {
+ return child.hasField(name.substring(dotIndex + 1));
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the value of the field with the given name.
+ *
+ * The return type may be a primitive type or a subclass of
+ * {@link RecordedObject}.
+ *
+ * It's possible to index into a nested object by using {@code "."} (for
+ * instance {@code "thread.group.parent.name}").
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * Example
+ *
+ *
+ *
+ * if (event.hasField("intValue")) {
+ * int intValue = event.getValue("intValue");
+ * System.out.println("Int value: " + intValue);
+ * }
+ *
+ * if (event.hasField("objectClass")) {
+ * RecordedClass clazz = event.getValue("objectClass");
+ * System.out.println("Class name: " + clazz.getName());
+ * }
+ *
+ * if (event.hasField("sampledThread")) {
+ * RecordedThread sampledThread = event.getValue("sampledThread");
+ * System.out.println("Sampled thread: " + sampledThread.getName());
+ * }
+ *
+ *
+ *
+ * @param the return type
+ * @param name of the field to get, not {@code null}
+ * @throws IllegalArgumentException if no field called {@code name} exists
+ *
+ * @return the value, can be {@code null}
+ *
+ * @see #hasField(String)
+ *
+ */
+ final public T getValue(String name) {
+ @SuppressWarnings("unchecked")
+ T t = (T) getValue(name, false);
+ return t;
+ }
+
+ private Object getValue(String name, boolean allowUnsigned) {
+ Objects.requireNonNull(name);
+ int index = 0;
+ for (ValueDescriptor v : descriptors) {
+ if (name.equals(v.getName())) {
+ Object object = objects[index];
+ if (object == null) {
+ // error or missing
+ return null;
+ }
+ if (v.getFields().isEmpty()) {
+ if (allowUnsigned && PrivateAccess.getInstance().isUnsigned(v)) {
+ // Types that are meaningless to widen
+ if (object instanceof Character || object instanceof Long) {
+ return object;
+ }
+ return new UnsignedValue(object);
+ }
+ return object; // primitives and primitive arrays
+ } else {
+ if (object instanceof RecordedObject) {
+ // known types from factory
+ return object;
+ }
+ // must be array type
+ Object[] array = (Object[]) object;
+ if (v.isArray()) {
+ // struct array
+ return structifyArray(v, array, 0);
+ }
+ // struct
+ return new RecordedObject(v.getFields(), (Object[]) object, timeConverter);
+ }
+ }
+ index++;
+ }
+
+ int dotIndex = name.indexOf(".");
+ if (dotIndex > 0) {
+ String structName = name.substring(0, dotIndex);
+ for (ValueDescriptor v : descriptors) {
+ if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
+ RecordedObject child = getValue(structName);
+ String subName = name.substring(dotIndex + 1);
+ if (child != null) {
+ return child.getValue(subName, allowUnsigned);
+ } else {
+ // Call getValueDescriptor to trigger IllegalArgumentException if the name is
+ // incorrect. Type can't be validate due to type erasure
+ getValueDescriptor(v.getFields(), subName, null);
+ throw new NullPointerException("Field value for \"" + structName + "\" was null. Can't access nested field \"" + subName + "\"");
+ }
+ }
+ }
+ }
+ throw new IllegalArgumentException("Could not find field with name " + name);
+ }
+
+ // Returns the leaf value descriptor matches both name or value, or throws an
+ // IllegalArgumentException
+ private ValueDescriptor getValueDescriptor(List descriptors, String name, String leafType) {
+ int dotIndex = name.indexOf(".");
+ if (dotIndex > 0) {
+ String first = name.substring(0, dotIndex);
+ String second = name.substring(dotIndex + 1);
+ for (ValueDescriptor v : descriptors) {
+ if (v.getName().equals(first)) {
+ List fields = v.getFields();
+ if (!fields.isEmpty()) {
+ return getValueDescriptor(v.getFields(), second, leafType);
+ }
+ }
+ }
+ throw new IllegalArgumentException("Attempt to get unknown field \"" + first + "\"");
+ }
+ for (ValueDescriptor v : descriptors) {
+ if (v.getName().equals(name)) {
+ if (leafType != null && !v.getTypeName().equals(leafType)) {
+ throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal data type conversion " + leafType);
+ }
+ return v;
+ }
+ }
+ throw new IllegalArgumentException("\"Attempt to get unknown field \"" + name + "\"");
+ }
+
+ // Gets a value, but checks that type and name is correct first
+ // This is to prevent a call to getString on a thread field, that is
+ // null to succeed.
+ private T getTypedValue(String name, String typeName) {
+ Objects.requireNonNull(name);
+ // Validate name and type first
+ getValueDescriptor(descriptors, name, typeName);
+ return getValue(name);
+ }
+
+ private Object[] structifyArray(ValueDescriptor v, Object[] array, int dimension) {
+ if (array == null) {
+ return null;
+ }
+ Object[] structArray = new Object[array.length];
+ for (int i = 0; i < structArray.length; i++) {
+ Object arrayElement = array[i];
+ if (dimension == 0) {
+ // No general way to handle structarrays
+ // without invoking ObjectFactory for every instance (which may require id)
+ if (isStackFrameType(v.getTypeName())) {
+ structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter);
+ } else {
+ structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter);
+ }
+ } else {
+ structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
+ }
+ }
+ return structArray;
+ }
+
+ private boolean isStackFrameType(String typeName) {
+ if (ObjectFactory.STACK_FRAME_VERSION_1.equals(typeName)) {
+ return true;
+ }
+ if (ObjectFactory.STACK_FRAME_VERSION_2.equals(typeName)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns an immutable list of the fields for this object.
+ *
+ * @return the fields, not {@code null}
+ */
+ public List getFields() {
+ return descriptors;
+ }
+
+ /**
+ * Returns the value of a field of type {@code boolean}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name name of the field to get, not {@code null}
+ *
+ * @return the value of the field, {@code true} or {@code false}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field is
+ * not of type {@code boolean}
+ *
+ * @see #hasField(String)
+ * @see #getValue(String)
+ */
+ public final boolean getBoolean(String name) {
+ Object o = getValue(name);
+ if (o instanceof Boolean) {
+ return ((Boolean) o).booleanValue();
+ }
+ throw newIllegalArgumentException(name, "boolean");
+ }
+
+ /**
+ * Returns the value of a field of type {@code byte}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "foo.bar"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field is
+ * not of type {@code byte}
+ *
+ * @see #hasField(String)
+ * @see #getValue(String)
+ */
+ public final byte getByte(String name) {
+ Object o = getValue(name);
+ if (o instanceof Byte) {
+ return ((Byte) o).byteValue();
+ }
+ throw newIllegalArgumentException(name, "byte");
+ }
+
+ /**
+ * Returns the value of a field of type {@code char}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field as a {@code char}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field is
+ * not of type {@code char}
+ *
+ * @see #hasField(String)
+ * @see #getValue(String)
+ */
+ public final char getChar(String name) {
+ Object o = getValue(name);
+ if (o instanceof Character) {
+ return ((Character) o).charValue();
+ }
+
+ throw newIllegalArgumentException(name, "char");
+ }
+
+ /**
+ * Returns the value of a field of type {@code short} or of another primitive
+ * type convertible to type {@code short} by a widening conversion.
+ *
+ * This method can be used on the following types: {@code short} and {@code byte}.
+ *
+ * If the field has the {@code @Unsigned} annotation and is of a narrower type
+ * than {@code short}, then the value is returned as an unsigned.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field converted to type {@code short}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * value can't be converted to the type {@code short} by a widening
+ * conversion
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final short getShort(String name) {
+ Object o = getValue(name, true);
+ if (o instanceof Short) {
+ return ((Short) o).shortValue();
+ }
+ if (o instanceof Byte) {
+ return ((Byte) o).byteValue();
+ }
+ if (o instanceof UnsignedValue) {
+ Object u = ((UnsignedValue) o).value();
+ if (u instanceof Short) {
+ return ((Short) u).shortValue();
+ }
+ if (u instanceof Byte) {
+ return (short) Byte.toUnsignedInt(((Byte) u));
+ }
+ }
+ throw newIllegalArgumentException(name, "short");
+ }
+
+ /**
+ * Returns the value of a field of type {@code int} or of another primitive type
+ * that is convertible to type {@code int} by a widening conversion.
+ *
+ * This method can be used on fields of the following types: {@code int},
+ * {@code short}, {@code char}, and {@code byte}.
+ *
+ * If the field has the {@code @Unsigned} annotation and is of a narrower type
+ * than {@code int}, then the value will be returned as an unsigned.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field converted to type {@code int}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * value can't be converted to the type {@code int} by a widening
+ * conversion
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final int getInt(String name) {
+ Object o = getValue(name, true);
+ if (o instanceof Integer) {
+ return ((Integer) o).intValue();
+ }
+ if (o instanceof Short) {
+ return ((Short) o).intValue();
+ }
+ if (o instanceof Character) {
+ return ((Character) o).charValue();
+ }
+ if (o instanceof Byte) {
+ return ((Byte) o).intValue();
+ }
+ if (o instanceof UnsignedValue) {
+ Object u = ((UnsignedValue) o).value();
+ if (u instanceof Integer) {
+ return ((Integer) u).intValue();
+ }
+ if (u instanceof Short) {
+ return Short.toUnsignedInt(((Short) u));
+ }
+ if (u instanceof Byte) {
+ return Byte.toUnsignedInt(((Byte) u));
+ }
+ }
+ throw newIllegalArgumentException(name, "int");
+ }
+
+ /**
+ * Returns the value of a field of type {@code float} or of another primitive
+ * type convertible to type {@code float} by a widening conversion.
+ *
+ * This method can be used on fields of the following types: {@code float},
+ * {@code long}, {@code int}, {@code short}, {@code char}, and {@code byte}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field converted to type {@code float}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * value can't be converted to the type {@code float} by a widening
+ * conversion
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final float getFloat(String name) {
+ Object o = getValue(name);
+ if (o instanceof Float) {
+ return ((Float) o).floatValue();
+ }
+ if (o instanceof Long) {
+ return ((Long) o).floatValue();
+ }
+ if (o instanceof Integer) {
+ return ((Integer) o).floatValue();
+ }
+ if (o instanceof Short) {
+ return ((Short) o).floatValue();
+ }
+ if (o instanceof Byte) {
+ return ((Byte) o).byteValue();
+ }
+ if (o instanceof Character) {
+ return ((Character) o).charValue();
+ }
+ throw newIllegalArgumentException(name, "float");
+ }
+
+ /**
+ * Returns the value of a field of type {@code long} or of another primitive
+ * type that is convertible to type {@code long} by a widening conversion.
+ *
+ * This method can be used on fields of the following types: {@code long},
+ * {@code int}, {@code short}, {@code char}, and {@code byte}.
+ *
+ * If the field has the {@code @Unsigned} annotation and is of a narrower type
+ * than {@code long}, then the value will be returned as an unsigned.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field converted to type {@code long}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * value can't be converted to the type {@code long} via a widening
+ * conversion
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final long getLong(String name) {
+ Object o = getValue(name, true);
+ if (o instanceof Long) {
+ return ((Long) o).longValue();
+ }
+ if (o instanceof Integer) {
+ return ((Integer) o).longValue();
+ }
+ if (o instanceof Short) {
+ return ((Short) o).longValue();
+ }
+ if (o instanceof Character) {
+ return ((Character) o).charValue();
+ }
+ if (o instanceof Byte) {
+ return ((Byte) o).longValue();
+ }
+ if (o instanceof UnsignedValue) {
+ Object u = ((UnsignedValue) o).value();
+ if (u instanceof Integer) {
+ return Integer.toUnsignedLong(((Integer) u));
+ }
+ if (u instanceof Short) {
+ return Short.toUnsignedLong(((Short) u));
+ }
+ if (u instanceof Byte) {
+ return Byte.toUnsignedLong(((Byte) u));
+ }
+ }
+ throw newIllegalArgumentException(name, "long");
+ }
+
+ /**
+ * Returns the value of a field of type {@code double} or of another primitive
+ * type that is convertible to type {@code double} by a widening conversion.
+ *
+ * This method can be used on fields of the following types: {@code double}, {@code float},
+ * {@code long}, {@code int}, {@code short}, {@code char}, and {@code byte}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field converted to type {@code double}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * value can't be converted to the type {@code double} by a widening
+ * conversion
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final double getDouble(String name) {
+ Object o = getValue(name);
+ if (o instanceof Double) {
+ return ((Double) o).doubleValue();
+ }
+ if (o instanceof Float) {
+ return ((Float) o).doubleValue();
+ }
+ if (o instanceof Long) {
+ return ((Long) o).doubleValue();
+ }
+ if (o instanceof Integer) {
+ return ((Integer) o).doubleValue();
+ }
+ if (o instanceof Short) {
+ return ((Short) o).doubleValue();
+ }
+ if (o instanceof Byte) {
+ return ((Byte) o).byteValue();
+ }
+ if (o instanceof Character) {
+ return ((Character) o).charValue();
+ }
+ throw newIllegalArgumentException(name, "double");
+ }
+
+ /**
+ * Returns the value of a field of type {@code String}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "foo.bar"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field as a {@code String}, can be {@code null}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * isn't of type {@code String}
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final String getString(String name) {
+ return getTypedValue(name, "java.lang.String");
+ }
+
+ /**
+ * Returns the value of a timespan field.
+ *
+ * This method can be used on fields annotated with {@code @Timespan}, and of
+ * the following types: {@code long}, {@code int}, {@code short}, {@code char},
+ * and {@code byte}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return a time span represented as a {@code Duration}, not {@code null}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * value can't be converted to a {@code Duration} object
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final Duration getDuration(String name) {
+ Object o = getValue(name);
+ if (o instanceof Long) {
+ return getDuration(((Long) o).longValue(), name);
+ }
+ if (o instanceof Integer) {
+ return getDuration(((Integer) o).longValue(), name);
+ }
+ if (o instanceof Short) {
+ return getDuration(((Short) o).longValue(), name);
+ }
+ if (o instanceof Character) {
+ return getDuration(((Character) o).charValue(), name);
+ }
+ if (o instanceof Byte) {
+ return getDuration(((Byte) o).longValue(), name);
+ }
+ if (o instanceof UnsignedValue) {
+ Object u = ((UnsignedValue) o).value();
+ if (u instanceof Integer) {
+ return getDuration(Integer.toUnsignedLong((Integer) u), name);
+ }
+ if (u instanceof Short) {
+ return getDuration(Short.toUnsignedLong((Short) u), name);
+ }
+ if (u instanceof Byte) {
+ return getDuration(Short.toUnsignedLong((Byte) u), name);
+ }
+ }
+ throw newIllegalArgumentException(name, "java,time.Duration");
+ }
+
+ private Duration getDuration(long timespan, String name) throws InternalError {
+ ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+ Timespan ts = v.getAnnotation(Timespan.class);
+ if (ts != null) {
+ switch (ts.value()) {
+ case Timespan.MICROSECONDS:
+ return Duration.ofNanos(1000 * timespan);
+ case Timespan.SECONDS:
+ return Duration.ofSeconds(timespan);
+ case Timespan.MILLISECONDS:
+ return Duration.ofMillis(timespan);
+ case Timespan.NANOSECONDS:
+ return Duration.ofNanos(timespan);
+ case Timespan.TICKS:
+ return Duration.ofNanos(timeConverter.convertTimespan(timespan));
+ }
+ throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
+ }
+ throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timespan");
+ }
+
+ /**
+ * Returns the value of a timestamp field.
+ *
+ * This method can be used on fields annotated with {@code @Timestamp}, and of
+ * the following types: {@code long}, {@code int}, {@code short}, {@code char}
+ * and {@code byte}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return a timstamp represented as an {@code Instant}, not {@code null}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * value can't be converted to an {@code Instant} object
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final Instant getInstant(String name) {
+ Object o = getValue(name, true);
+ if (o instanceof Long) {
+ return getInstant(((Long) o).longValue(), name);
+ }
+ if (o instanceof Integer) {
+ return getInstant(((Integer) o).longValue(), name);
+ }
+ if (o instanceof Short) {
+ return getInstant(((Short) o).longValue(), name);
+ }
+ if (o instanceof Character) {
+ return getInstant(((Character) o).charValue(), name);
+ }
+ if (o instanceof Byte) {
+ return getInstant(((Byte) o).longValue(), name);
+ }
+ if (o instanceof UnsignedValue) {
+ Object u = ((UnsignedValue) o).value();
+ if (u instanceof Integer) {
+ return getInstant(Integer.toUnsignedLong((Integer) u), name);
+ }
+ if (u instanceof Short) {
+ return getInstant(Short.toUnsignedLong((Short) u), name);
+ }
+ if (u instanceof Byte) {
+ return getInstant(Short.toUnsignedLong((Byte) u), name);
+ }
+ }
+ throw newIllegalArgumentException(name, "java,time.Instant");
+ }
+
+ private Instant getInstant(long timestamp, String name) {
+ ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+ Timestamp ts = v.getAnnotation(Timestamp.class);
+ if (ts != null) {
+ switch (ts.value()) {
+ case Timestamp.MILLISECONDS_SINCE_EPOCH:
+ return Instant.ofEpochMilli(timestamp);
+ case Timestamp.TICKS:
+ return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp));
+ }
+ throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
+ }
+ throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timestamp");
+ }
+
+ /**
+ * Returns the value of a field of type {@code Class}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "aaa.bbb"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field as a {@code RecordedClass}, can be
+ * {@code null}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * isn't of type {@code Class}
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final RecordedClass getClass(String name) {
+ return getTypedValue(name, "java.lang.Class");
+ }
+
+ /**
+ * Returns the value of a field of type {@code Thread}.
+ *
+ * It's possible to index into a nested object using {@code "."} (for example,
+ * {@code "foo.bar"}).
+ *
+ * A field might change or be removed in a future JDK release. A best practice
+ * for callers of this method is to validate the field before attempting access.
+ *
+ * @param name of the field to get, not {@code null}
+ *
+ * @return the value of the field as a {@code RecordedThread} object, can be
+ * {@code null}
+ *
+ * @throws IllegalArgumentException if the field doesn't exist, or the field
+ * isn't of type {@code Thread}
+ *
+ * @see #hasField(String)
+ * @set #getValue(String)
+ */
+ public final RecordedThread getThread(String name) {
+ return getTypedValue(name, "java.lang.Thread");
+ }
+
+ /**
+ * Returns a textual representation of this object.
+ *
+ * @return textual description of this object
+ */
+ @Override
+ final public String toString() {
+ StringWriter s = new StringWriter();
+ PrettyWriter p = new PrettyWriter(new PrintWriter(s));
+ try {
+ if (this instanceof RecordedEvent) {
+ p.print((RecordedEvent) this);
+ } else {
+ p.print(this, "");
+ }
+
+ } catch (IOException e) {
+ // Ignore, should not happen with StringWriter
+ }
+ p.flush();
+ return s.toString();
+ }
+
+ private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
+ return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedStackTrace.java b/src/share/classes/jdk/jfr/consumer/RecordedStackTrace.java
new file mode 100644
index 0000000000000000000000000000000000000000..377176f09dcf93669f0b469a60b69da12b593700
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedStackTrace.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.Type;
+
+/**
+ * A recorded stack trace.
+ *
+ * @since 9
+ */
+public final class RecordedStackTrace extends RecordedObject {
+
+ static ObjectFactory createFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory(type) {
+ @Override
+ RecordedStackTrace createTyped(List desc, long id, Object[] object) {
+ return new RecordedStackTrace(desc, object, timeConverter);
+ }
+ };
+ }
+
+ private RecordedStackTrace(List desc, Object[] values, TimeConverter timeConverter) {
+ super(desc, values, timeConverter);
+ }
+
+ /**
+ * Returns the frames in the stack trace.
+ *
+ * @return a list of Java stack frames, not {@code null}
+ */
+ @SuppressWarnings("unchecked")
+ public List getFrames() {
+ Object[] array = getTyped("frames", Object[].class, null);
+ if (array == null) {
+ return Collections.EMPTY_LIST;
+ }
+ List> list = Arrays.asList(array);
+ return (List) list;
+ }
+
+ /**
+ * Returns {@code true} if the stack trace is truncated due to its size,
+ * {@code false} otherwise.
+ *
+ * @return {@code true} if the stack trace is truncated, {@code false}
+ * otherwise
+ */
+ public boolean isTruncated() {
+ return getTyped("truncated", Boolean.class, true);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedThread.java b/src/share/classes/jdk/jfr/consumer/RecordedThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..95292ad77076c7b18fe2bc7743cbb8e64732e2f3
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedThread.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.util.List;
+
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.Type;
+
+/**
+ * A recorded thread.
+ *
+ * @since 9
+ */
+public final class RecordedThread extends RecordedObject {
+
+ static ObjectFactory createFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory(type) {
+ @Override
+ RecordedThread createTyped(List desc, long id, Object[] object) {
+ return new RecordedThread(desc, id, object, timeConverter);
+ }
+ };
+ }
+
+ private final long uniqueId;
+
+ private RecordedThread(List descriptors, long id, Object[] values, TimeConverter timeConverter) {
+ super(descriptors, values, timeConverter);
+ this.uniqueId = id;
+ }
+
+ /**
+ * Returns the thread name used by the operating system.
+ *
+ * @return the OS thread name, or {@code null} if doesn't exist
+ */
+ public String getOSName() {
+ return getTyped("osName", String.class, null);
+ }
+
+ /**
+ * Returns the thread ID used by the operating system.
+ *
+ * @return The Java thread ID, or {@code -1} if doesn't exist
+ */
+ public long getOSThreadId() {
+ Long l = getTyped("osThreadId", Long.class, -1L);
+ return l.longValue();
+ }
+
+ /**
+ * Returns the Java thread group, if available.
+ *
+ * @return the thread group, or {@code null} if doesn't exist
+ */
+ public RecordedThreadGroup getThreadGroup() {
+ return getTyped("group", RecordedThreadGroup.class, null);
+ }
+
+ /**
+ * Returns the Java thread name, or {@code null} if if doesn't exist.
+ *
+ * Returns {@code java.lang.Thread.getName()} if the thread has a Java
+ * representation. {@code null} otherwise.
+ *
+ * @return the Java thread name, or {@code null} if doesn't exist
+ */
+ public String getJavaName() {
+ return getTyped("javaName", String.class, null);
+ }
+
+ /**
+ * Returns the Java thread ID, or {@code -1} if it's not a Java thread.
+ *
+ * @return the Java thread ID, or {@code -1} if it's not a Java thread
+ */
+ public long getJavaThreadId() {
+ Long l = getTyped("javaThreadId", Long.class, -1L);
+ return l.longValue();
+ }
+
+ /**
+ * Returns a unique ID for both native threads and Java threads that can't be
+ * reused within the lifespan of the JVM.
+ *
+ * See {@link #getJavaThreadId()} for the ID that is returned by
+ * {@code java.lang.Thread.getId()}
+ *
+ * @return a unique ID for the thread
+ */
+ public long getId() {
+ return uniqueId;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java b/src/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..f363f4778ab8e4a5ef8c0137a16f81bb8c41e5e9
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.util.List;
+
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.Type;
+
+/**
+ * A recorded Java thread group.
+ *
+ * @since 9
+ */
+public final class RecordedThreadGroup extends RecordedObject {
+
+ static ObjectFactory createFactory(Type type, TimeConverter timeConverter) {
+ return new ObjectFactory(type) {
+ @Override
+ RecordedThreadGroup createTyped(List desc, long id, Object[] object) {
+ return new RecordedThreadGroup(desc, object, timeConverter);
+ }
+ };
+ }
+
+ private RecordedThreadGroup(List descriptors, Object[] objects, TimeConverter timeConverter) {
+ super(descriptors, objects, timeConverter);
+ }
+
+ /**
+ * Returns the name of the thread group, or {@code null} if doesn't exist.
+ *
+ * @return the thread group name, or {@code null} if doesn't exist
+ */
+ public String getName() {
+ return getTyped("name", String.class, null);
+ }
+
+ /**
+ * Returns the parent thread group, or {@code null} if it doesn't exist.
+ *
+ * @return parent thread group, or {@code null} if it doesn't exist.
+ */
+ public RecordedThreadGroup getParent() {
+ return getTyped("parent", RecordedThreadGroup.class, null);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/RecordingFile.java b/src/share/classes/jdk/jfr/consumer/RecordingFile.java
new file mode 100644
index 0000000000000000000000000000000000000000..dde93ed81d8a74fc1e2096198886b35d7d1087b1
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/RecordingFile.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * A recording file.
+ *
+ * The following example shows how read and print all events in a recording file.
+ *
+ *
+ *
+ * try (RecordingFile recordingFile = new RecordingFile(Paths.get("recording.jfr"))) {
+ * while (recordingFile.hasMoreEvents()) {
+ * RecordedEvent event = recordingFile.readEvent();
+ * System.out.println(event);
+ * }
+ * }
+ *
+ *
+ *
+ * @since 9
+ */
+public final class RecordingFile implements Closeable {
+
+ private final File file;
+ private RecordingInput input;
+ private ChunkParser chunkParser;
+ private RecordedEvent nextEvent;
+ private boolean eof;
+
+ /**
+ * Creates a recording file.
+ *
+ * @param file the path of the file to open, not {@code null}
+ * @throws IOException if it's not a valid recording file, or an I/O error
+ * occurred
+ * @throws NoSuchFileException if the {@code file} can't be located
+ *
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkRead} method denies read access to the file.
+ */
+ public RecordingFile(Path file) throws IOException {
+ this.file = file.toFile();
+ this.input = new RecordingInput(this.file);
+ findNext();
+ }
+
+ /**
+ * Reads the next event in the recording.
+ *
+ * @return the next event, not {@code null}
+ *
+ * @throws EOFException if no more events exist in the recording file
+ * @throws IOException if an I/O error occurs.
+ *
+ * @see #hasMoreEvents()
+ */
+ public RecordedEvent readEvent() throws IOException {
+ if (eof) {
+ ensureOpen();
+ throw new EOFException();
+ }
+ RecordedEvent event = nextEvent;
+ nextEvent = chunkParser.readEvent();
+ if (nextEvent == null) {
+ findNext();
+ }
+ return event;
+ }
+
+ /**
+ * Returns {@code true} if unread events exist in the recording file,
+ * {@code false} otherwise.
+ *
+ * @return {@code true} if unread events exist in the recording, {@code false}
+ * otherwise.
+ */
+ public boolean hasMoreEvents() {
+ return !eof;
+ }
+
+ /**
+ * Returns a list of all event types in this recording.
+ *
+ * @return a list of event types, not {@code null}
+ * @throws IOException if an I/O error occurred while reading from the file
+ *
+ * @see #hasMoreEvents()
+ */
+ public List readEventTypes() throws IOException {
+ ensureOpen();
+ List types = new ArrayList<>();
+ HashSet foundIds = new HashSet<>();
+ try (RecordingInput ri = new RecordingInput(file)) {
+ ChunkHeader ch = new ChunkHeader(ri);
+ aggregateTypeForChunk(ch, types, foundIds);
+ while (!ch.isLastChunk()) {
+ ch = ch.nextHeader();
+ aggregateTypeForChunk(ch, types, foundIds);
+ }
+ }
+ return types;
+ }
+
+ private static void aggregateTypeForChunk(ChunkHeader ch, List types, HashSet foundIds) throws IOException {
+ MetadataDescriptor m = ch.readMetadata();
+ for (EventType t : m.getEventTypes()) {
+ if (!foundIds.contains(t.getId())) {
+ types.add(t);
+ foundIds.add(t.getId());
+ }
+ }
+ }
+
+ /**
+ * Closes this recording file and releases any system resources that are
+ * associated with it.
+ *
+ * @throws IOException if an I/O error occurred
+ */
+ public void close() throws IOException {
+ if (input != null) {
+ eof = true;
+ input.close();
+ chunkParser = null;
+ input = null;
+ nextEvent = null;
+ }
+ }
+
+ /**
+ * Returns a list of all events in a file.
+ *
+ * This method is intended for simple cases where it's convenient to read all
+ * events in a single operation. It isn't intended for reading large files.
+ *
+ * @param path the path to the file, not {@code null}
+ *
+ * @return the events from the file as a {@code List} object; whether the
+ * {@code List} is modifiable or not is implementation dependent and
+ * therefore not specified, not {@code null}
+ *
+ * @throws IOException if an I/O error occurred, it's not a Flight Recorder
+ * file or a version of a JFR file that can't be parsed
+ *
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkRead} method denies read access to the file.
+ */
+ public static List readAllEvents(Path path) throws IOException {
+ try (RecordingFile r = new RecordingFile(path)) {
+ List list = new ArrayList<>();
+ while (r.hasMoreEvents()) {
+ list.add(r.readEvent());
+ }
+ return list;
+ }
+ }
+
+ // either sets next to an event or sets eof to true
+ private void findNext() throws IOException {
+ while (nextEvent == null) {
+ if (chunkParser == null) {
+ chunkParser = new ChunkParser(input);
+ } else if (!chunkParser.isLastChunk()) {
+ chunkParser = chunkParser.nextChunkParser();
+ } else {
+ eof = true;
+ return;
+ }
+ nextEvent = chunkParser.readEvent();
+ }
+ }
+
+ private void ensureOpen() throws IOException {
+ if (input == null) {
+ throw new IOException("Stream Closed");
+ }
+ }
+}
diff --git a/src/share/classes/jdk/jfr/consumer/TimeConverter.java b/src/share/classes/jdk/jfr/consumer/TimeConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3b625eb1b4a01f8596e7196e639e15a76635c2d
--- /dev/null
+++ b/src/share/classes/jdk/jfr/consumer/TimeConverter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.consumer;
+
+import jdk.jfr.internal.consumer.ChunkHeader;
+
+/**
+ * Converts ticks to nanoseconds
+ */
+final class TimeConverter {
+ private final long startTicks;
+ private final long startNanos;
+ private final double divisor;
+
+ TimeConverter(ChunkHeader chunkHeader) {
+ this.startTicks = chunkHeader.getStartTicks();
+ this.startNanos = chunkHeader.getStartNanos();
+ this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
+ }
+
+ public long convertTimestamp(long ticks) {
+ return startNanos + (long) ((ticks - startTicks) / divisor);
+ }
+
+ public long convertTimespan(long ticks) {
+ return (long) (ticks / divisor);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/events/AbstractJDKEvent.java b/src/share/classes/jdk/jfr/events/AbstractJDKEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1043480c79da9db6047185febd0c9d9e8f08caf
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/AbstractJDKEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.Registered;
+import jdk.jfr.StackTrace;
+
+@Registered(false)
+@Enabled(false)
+@StackTrace(false)
+abstract class AbstractJDKEvent extends Event {
+}
diff --git a/src/share/classes/jdk/jfr/events/ActiveRecordingEvent.java b/src/share/classes/jdk/jfr/events/ActiveRecordingEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..631315e43c8e0b495607e0d52460840d8d74c098
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/ActiveRecordingEvent.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.DataAmount;
+import jdk.jfr.Name;
+import jdk.jfr.StackTrace;
+import jdk.jfr.Timespan;
+import jdk.jfr.Timestamp;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "ActiveRecording")
+@Label("Flight Recording")
+@Category("Flight Recorder")
+@StackTrace(false)
+public final class ActiveRecordingEvent extends AbstractJDKEvent {
+
+ @Label("Id")
+ public long id;
+
+ @Label("Name")
+ public String name;
+
+ @Label("Destination")
+ public String destination;
+
+ @Label("Max Age")
+ @Timespan(Timespan.MILLISECONDS)
+ public long maxAge;
+
+ @Label("Max Size")
+ @DataAmount
+ public long maxSize;
+
+ @Label("Start Time")
+ @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
+ public long recordingStart;
+
+ @Label("Recording Duration")
+ @Timespan(Timespan.MILLISECONDS)
+ public long recordingDuration;
+}
diff --git a/src/share/classes/jdk/jfr/events/ActiveSettingEvent.java b/src/share/classes/jdk/jfr/events/ActiveSettingEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c51db6bae818e644c387136581ae30ce3b18ee0
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/ActiveSettingEvent.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import jdk.jfr.StackTrace;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "ActiveSetting")
+@Label("Recording Setting")
+@Category("Flight Recorder")
+@StackTrace(false)
+public final class ActiveSettingEvent extends AbstractJDKEvent {
+
+ @Label("Event Id")
+ public long id;
+
+ @Label("Setting Name")
+ public String name;
+
+ @Label("Setting Value")
+ public String value;
+}
diff --git a/src/share/classes/jdk/jfr/events/ErrorThrownEvent.java b/src/share/classes/jdk/jfr/events/ErrorThrownEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..64efc7049df8f23e4cc68118ce79b58cc659c9c4
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/ErrorThrownEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "JavaErrorThrow")
+@Label("Java Error")
+@Category("Java Application")
+@Description("An object derived from java.lang.Error has been created. OutOfMemoryErrors are ignored")
+public final class ErrorThrownEvent extends AbstractJDKEvent {
+
+ @Label("Message")
+ public String message;
+
+ @Label("Class")
+ public Class> thrownClass;
+}
diff --git a/src/share/classes/jdk/jfr/events/ExceptionStatisticsEvent.java b/src/share/classes/jdk/jfr/events/ExceptionStatisticsEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a63019a469b6cc70c843542be0eac893c9bddca
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/ExceptionStatisticsEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import jdk.jfr.StackTrace;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "ExceptionStatistics")
+@Label("Exception Statistics")
+@Category({ "Java Application", "Statistics" })
+@Description("Number of objects derived from java.lang.Throwable that have been created")
+@StackTrace(false)
+public final class ExceptionStatisticsEvent extends AbstractJDKEvent {
+
+ @Label("Exceptions Created")
+ public long throwables;
+}
diff --git a/src/share/classes/jdk/jfr/events/ExceptionThrownEvent.java b/src/share/classes/jdk/jfr/events/ExceptionThrownEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..1489803ee89fd7302fe6c32b1e9148e90c053e36
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/ExceptionThrownEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "JavaExceptionThrow")
+@Label("Java Exception")
+@Category("Java Application")
+@Description("An object derived from java.lang.Exception has been created")
+public final class ExceptionThrownEvent extends AbstractJDKEvent {
+
+ @Label("Message")
+ public String message;
+
+ @Label("Class")
+ public Class> thrownClass;
+}
diff --git a/src/share/classes/jdk/jfr/events/FileForceEvent.java b/src/share/classes/jdk/jfr/events/FileForceEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5b62a91a12fd695085170a561aa455df3810397
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/FileForceEvent.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "FileForce")
+@Label("File Force")
+@Category("Java Application")
+@Description("Force updates to be written to file")
+public final class FileForceEvent extends AbstractJDKEvent {
+
+ public static final ThreadLocal EVENT =
+ new ThreadLocal() {
+ @Override protected FileForceEvent initialValue() {
+ return new FileForceEvent();
+ }
+ };
+
+ @Label("Path")
+ @Description("Full path of the file")
+ public String path;
+
+ @Label("Update Metadata")
+ @Description("Whether the file metadata is updated")
+ public boolean metaData;
+
+ public void reset() {
+ path = null;
+ metaData = false;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/events/FileReadEvent.java b/src/share/classes/jdk/jfr/events/FileReadEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..f868f05bc5ce562cb6366d4ae83a62efec1b9640
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/FileReadEvent.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.DataAmount;
+import jdk.jfr.Name;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "FileRead")
+@Label("File Read")
+@Category("Java Application")
+@Description("Reading data from a file")
+public final class FileReadEvent extends AbstractJDKEvent {
+
+ public static final ThreadLocal EVENT =
+ new ThreadLocal() {
+ @Override protected FileReadEvent initialValue() {
+ return new FileReadEvent();
+ }
+ };
+
+ @Label("Path")
+ @Description("Full path of the file")
+ public String path;
+
+ @Label("Bytes Read")
+ @Description("Number of bytes read from the file (possibly 0)")
+ @DataAmount
+ public long bytesRead;
+
+ @Label("End of File")
+ @Description("If end of file was reached")
+ public boolean endOfFile;
+
+ public void reset() {
+ path = null;
+ endOfFile = false;
+ bytesRead = 0;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/events/FileWriteEvent.java b/src/share/classes/jdk/jfr/events/FileWriteEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0a9fbfd95265a7fddd50776d600d330fb4ad553
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/FileWriteEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.DataAmount;
+import jdk.jfr.Name;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "FileWrite")
+@Label("File Write")
+@Category("Java Application")
+@Description("Writing data to a file")
+public final class FileWriteEvent extends AbstractJDKEvent {
+
+ public static final ThreadLocal EVENT =
+ new ThreadLocal() {
+ @Override protected FileWriteEvent initialValue() {
+ return new FileWriteEvent();
+ }
+ };
+
+ @Label("Path")
+ @Description("Full path of the file")
+ public String path;
+
+ @Label("Bytes Written")
+ @Description("Number of bytes written to the file")
+ @DataAmount
+ public long bytesWritten;
+
+ public void reset() {
+ path = null;
+ bytesWritten = 0;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/events/SocketReadEvent.java b/src/share/classes/jdk/jfr/events/SocketReadEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..91db94c6f4988481436ee5eb636fadb1ed61d861
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/SocketReadEvent.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.DataAmount;
+import jdk.jfr.Name;
+import jdk.jfr.Timespan;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "SocketRead")
+@Label("Socket Read")
+@Category("Java Application")
+@Description("Reading data from a socket")
+public final class SocketReadEvent extends AbstractJDKEvent {
+
+ public static final ThreadLocal EVENT =
+ new ThreadLocal() {
+ @Override protected SocketReadEvent initialValue() {
+ return new SocketReadEvent();
+ }
+ };
+
+ @Label("Remote Host")
+ public String host;
+
+ @Label("Remote Address")
+ public String address;
+
+ @Label("Remote Port")
+ public int port;
+
+ @Label("Timeout Value")
+ @Timespan(Timespan.MILLISECONDS)
+ public long timeout;
+
+ @Label("Bytes Read")
+ @Description("Number of bytes read from the socket")
+ @DataAmount
+ public long bytesRead;
+
+ @Label("End of Stream")
+ @Description("If end of stream was reached")
+ public boolean endOfStream;
+
+ public void reset() {
+ host = null;
+ address = null;
+ port = 0;
+ timeout = 0;
+ bytesRead = 0L;
+ endOfStream = false;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/events/SocketWriteEvent.java b/src/share/classes/jdk/jfr/events/SocketWriteEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a0caa36aa52d67a4e17da8c86212fa56207630e
--- /dev/null
+++ b/src/share/classes/jdk/jfr/events/SocketWriteEvent.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.events;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.DataAmount;
+import jdk.jfr.Name;
+import jdk.jfr.internal.Type;
+
+@Name(Type.EVENT_NAME_PREFIX + "SocketWrite")
+@Label("Socket Write")
+@Category("Java Application")
+@Description("Writing data to a socket")
+public final class SocketWriteEvent extends AbstractJDKEvent {
+
+ public static final ThreadLocal EVENT =
+ new ThreadLocal() {
+ @Override protected SocketWriteEvent initialValue() {
+ return new SocketWriteEvent();
+ }
+ };
+
+ @Label("Remote Host")
+ public String host;
+
+ @Label("Remote Address")
+ public String address;
+
+ @Label("Remote Port")
+ public int port;
+
+ @Label("Bytes Written")
+ @Description("Number of bytes written to the socket")
+ @DataAmount
+ public long bytesWritten;
+
+ public void reset() {
+ host = null;
+ address = null;
+ port = 0;
+ bytesWritten = 0;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/ASMToolkit.java b/src/share/classes/jdk/jfr/internal/ASMToolkit.java
new file mode 100644
index 0000000000000000000000000000000000000000..732507ba861a0f505b3db29f2aa6787adfad5541
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/ASMToolkit.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.List;
+
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.commons.Method;
+import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.EventInstrumentation.FieldInfo;
+
+final class ASMToolkit {
+ private static Type TYPE_STRING = Type.getType(String.class);
+ private static Type Type_THREAD = Type.getType(Thread.class);
+ private static Type TYPE_CLASS = Type.getType(Class.class);
+
+ public static void invokeSpecial(MethodVisitor methodVisitor, String className, Method m) {
+ methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, className, m.getName(), m.getDescriptor(), false);
+ }
+
+ public static void invokeStatic(MethodVisitor methodVisitor, String className, Method m) {
+ methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, className, m.getName(), m.getDescriptor(), false);
+ }
+
+ public static void invokeVirtual(MethodVisitor methodVisitor, String className, Method m) {
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, m.getName(), m.getDescriptor(), false);
+ }
+
+
+ public static Type toType(ValueDescriptor v) {
+ String typeName = v.getTypeName();
+
+ switch (typeName) {
+ case "byte":
+ return Type.BYTE_TYPE;
+ case "short":
+ return Type.SHORT_TYPE;
+ case "int":
+ return Type.INT_TYPE;
+ case "long":
+ return Type.LONG_TYPE;
+ case "double":
+ return Type.DOUBLE_TYPE;
+ case "float":
+ return Type.FLOAT_TYPE;
+ case "char":
+ return Type.CHAR_TYPE;
+ case "boolean":
+ return Type.BOOLEAN_TYPE;
+ case "java.lang.String":
+ return TYPE_STRING;
+ case "java.lang.Thread":
+ return Type_THREAD;
+ case "java.lang.Class":
+ return TYPE_CLASS;
+ }
+ // Add support for SettingControl?
+ throw new Error("Not a valid type " + v.getTypeName());
+ }
+
+ /**
+ * Converts "int" into "I" and "java.lang.String" into "Ljava/lang/String;"
+ *
+ * @param typeName
+ * type
+ *
+ * @return descriptor
+ */
+ public static String getDescriptor(String typeName) {
+ if ("int".equals(typeName)) {
+ return "I";
+ }
+ if ("long".equals(typeName)) {
+ return "J";
+ }
+ if ("boolean".equals(typeName)) {
+ return "Z";
+ }
+ if ("float".equals(typeName)) {
+ return "F";
+ }
+ if ("double".equals(typeName)) {
+ return "D";
+ }
+ if ("short".equals(typeName)) {
+ return "S";
+ }
+ if ("char".equals(typeName)) {
+ return "C";
+ }
+ if ("byte".equals(typeName)) {
+ return "B";
+ }
+ String internal = getInternalName(typeName);
+ return Type.getObjectType(internal).getDescriptor();
+ }
+
+ /**
+ * Converts java.lang.String into java/lang/String
+ *
+ * @param className
+ *
+ * @return internal name
+ */
+ public static String getInternalName(String className) {
+ return className.replace(".", "/");
+ }
+
+ public static Method makeWriteMethod(List fields) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ for (FieldInfo v : fields) {
+ if (!v.fieldName.equals(EventInstrumentation.FIELD_EVENT_THREAD) && !v.fieldName.equals(EventInstrumentation.FIELD_STACK_TRACE)) {
+ sb.append(v.fieldDescriptor);
+ }
+ }
+ sb.append(")V");
+ return new Method("write", sb.toString());
+ }
+
+ public static void logASM(String className, byte[] bytes) {
+ Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Generated bytecode for class " + className);
+ Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.TRACE, () -> {
+ ClassReader cr = new ClassReader(bytes);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintWriter w = new PrintWriter(baos);
+ w.println("Bytecode:");
+ cr.accept(new TraceClassVisitor(w), 0);
+ return baos.toString();
+ });
+ }
+
+}
diff --git a/src/share/classes/jdk/jfr/internal/AnnotationConstruct.java b/src/share/classes/jdk/jfr/internal/AnnotationConstruct.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3c594f68feb809ccbe6070ce91350505cf598cb
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/AnnotationConstruct.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.List;
+
+import jdk.jfr.AnnotationElement;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.Unsigned;
+
+public final class AnnotationConstruct {
+
+ private static final class AnnotationInvokationHandler implements InvocationHandler {
+
+ private final AnnotationElement annotationElement;
+
+ AnnotationInvokationHandler(AnnotationElement a) {
+ this.annotationElement = a;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ String methodName = method.getName();
+ int parameters = method.getTypeParameters().length;
+ if (parameters == 0 && annotationElement.hasValue(methodName)) {
+ return annotationElement.getValue(methodName);
+ }
+ throw new UnsupportedOperationException("Flight Recorder proxy only supports members declared in annotation interfaces, i.e. not toString, equals etc.");
+ }
+ }
+
+ private List annotationElements = Collections.emptyList();
+ private byte unsignedFlag = -1;
+ public AnnotationConstruct(List ann) {
+ this.annotationElements = ann;
+ }
+
+ public AnnotationConstruct() {
+ }
+
+ public void setAnnotationElements(List elements) {
+ annotationElements = Utils.smallUnmodifiable(elements);
+ }
+
+ public String getLabel() {
+ Label label = getAnnotation(Label.class);
+ if (label == null) {
+ return null;
+ }
+ return label.value();
+ }
+
+ public String getDescription() {
+ Description description = getAnnotation(Description.class);
+ if (description == null) {
+ return null;
+ }
+ return description.value();
+ }
+
+ @SuppressWarnings("unchecked")
+ public final T getAnnotation(Class extends Annotation> clazz) {
+ AnnotationElement ae = getAnnotationElement(clazz);
+ if (ae != null) {
+ return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class>[] { clazz }, new AnnotationInvokationHandler(ae));
+ }
+ return null;
+ }
+
+ public List getUnmodifiableAnnotationElements() {
+ return annotationElements;
+ }
+
+ // package private
+ boolean remove(AnnotationElement annotation) {
+ return annotationElements.remove(annotation);
+ }
+
+ private AnnotationElement getAnnotationElement(Class extends Annotation> clazz) {
+ // if multiple annotation elements with the same name exists, prioritize
+ // the one with the same id. Note, id alone is not a guarantee, since it
+ // may differ between JVM instances.
+ long id = Type.getTypeId(clazz);
+ String className = clazz.getName();
+ for (AnnotationElement a : getUnmodifiableAnnotationElements()) {
+ if (a.getTypeId() == id && a.getTypeName().equals(className)) {
+ return a;
+ }
+ }
+ for (AnnotationElement a : getUnmodifiableAnnotationElements()) {
+ if (a.getTypeName().equals(className)) {
+ return a;
+ }
+ }
+ return null;
+ }
+
+ public boolean hasUnsigned() {
+ // Must be initialized lazily since some annotation elements
+ // are added after construction
+ if (unsignedFlag < 0) {
+ Unsigned unsigned = getAnnotation(Unsigned.class);
+ unsignedFlag = (byte) (unsigned == null ? 0 :1);
+ }
+ return unsignedFlag == (byte)1 ? true : false;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/Bits.java b/src/share/classes/jdk/jfr/internal/Bits.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6b02505bc19f98ecb33850a9721d20d00dd9be0
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/Bits.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.nio.ByteOrder;
+
+import sun.misc.Unsafe;
+
+final class Bits { // package-private
+
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+ // XXX TODO proper value (e.g. copy from java.nio.Bits)
+ private static final boolean unalignedAccess = false/*unsafe.unalignedAccess()*/;
+ private static final boolean bigEndian = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
+
+ private Bits() { }
+
+ // -- Swapping --
+
+ private static short swap(short x) {
+ return Short.reverseBytes(x);
+ }
+
+ private static char swap(char x) {
+ return Character.reverseBytes(x);
+ }
+
+ private static int swap(int x) {
+ return Integer.reverseBytes(x);
+ }
+
+ private static long swap(long x) {
+ return Long.reverseBytes(x);
+ }
+
+ private static float swap(float x) {
+ return Float.intBitsToFloat(swap(Float.floatToIntBits(x)));
+ }
+
+ private static double swap(double x) {
+ return Double.longBitsToDouble(swap(Double.doubleToLongBits(x)));
+ }
+
+ // -- Alignment --
+
+ private static boolean isAddressAligned(long a, int datumSize) {
+ return (a & datumSize - 1) == 0;
+ }
+
+ // -- Primitives stored per byte
+
+ private static byte char1(char x) { return (byte)(x >> 8); }
+ private static byte char0(char x) { return (byte)(x ); }
+
+ private static byte short1(short x) { return (byte)(x >> 8); }
+ private static byte short0(short x) { return (byte)(x ); }
+
+ private static byte int3(int x) { return (byte)(x >> 24); }
+ private static byte int2(int x) { return (byte)(x >> 16); }
+ private static byte int1(int x) { return (byte)(x >> 8); }
+ private static byte int0(int x) { return (byte)(x ); }
+
+ private static byte long7(long x) { return (byte)(x >> 56); }
+ private static byte long6(long x) { return (byte)(x >> 48); }
+ private static byte long5(long x) { return (byte)(x >> 40); }
+ private static byte long4(long x) { return (byte)(x >> 32); }
+ private static byte long3(long x) { return (byte)(x >> 24); }
+ private static byte long2(long x) { return (byte)(x >> 16); }
+ private static byte long1(long x) { return (byte)(x >> 8); }
+ private static byte long0(long x) { return (byte)(x ); }
+
+ private static void putCharBigEndianUnaligned(long a, char x) {
+ putByte_(a , char1(x));
+ putByte_(a + 1, char0(x));
+ }
+
+ private static void putShortBigEndianUnaligned(long a, short x) {
+ putByte_(a , short1(x));
+ putByte_(a + 1, short0(x));
+ }
+
+ private static void putIntBigEndianUnaligned(long a, int x) {
+ putByte_(a , int3(x));
+ putByte_(a + 1, int2(x));
+ putByte_(a + 2, int1(x));
+ putByte_(a + 3, int0(x));
+ }
+
+ private static void putLongBigEndianUnaligned(long a, long x) {
+ putByte_(a , long7(x));
+ putByte_(a + 1, long6(x));
+ putByte_(a + 2, long5(x));
+ putByte_(a + 3, long4(x));
+ putByte_(a + 4, long3(x));
+ putByte_(a + 5, long2(x));
+ putByte_(a + 6, long1(x));
+ putByte_(a + 7, long0(x));
+ }
+
+ private static void putFloatBigEndianUnaligned(long a, float x) {
+ putIntBigEndianUnaligned(a, Float.floatToRawIntBits(x));
+ }
+
+ private static void putDoubleBigEndianUnaligned(long a, double x) {
+ putLongBigEndianUnaligned(a, Double.doubleToRawLongBits(x));
+ }
+
+ private static void putByte_(long a, byte b) {
+ unsafe.putByte(a, b);
+ }
+
+ private static void putBoolean_(long a, boolean x) {
+ unsafe.putBoolean(null, a, x);
+ }
+
+ private static void putChar_(long a, char x) {
+ unsafe.putChar(a, bigEndian ? x : swap(x));
+ }
+
+ private static void putShort_(long a, short x) {
+ unsafe.putShort(a, bigEndian ? x : swap(x));
+ }
+
+ private static void putInt_(long a, int x) {
+ unsafe.putInt(a, bigEndian ? x : swap(x));
+ }
+
+ private static void putLong_(long a, long x) {
+ unsafe.putLong(a, bigEndian ? x : swap(x));
+ }
+
+ private static void putFloat_(long a, float x) {
+ unsafe.putFloat(a, bigEndian ? x : swap(x));
+ }
+
+ private static void putDouble_(long a, double x) {
+ unsafe.putDouble(a, bigEndian ? x : swap(x));
+ }
+
+ // external api
+ static int putByte(long a, byte x) {
+ putByte_(a, x);
+ return Byte.BYTES;
+ }
+
+ static int putBoolean(long a, boolean x) {
+ putBoolean_(a, x);
+ return Byte.BYTES;
+ }
+
+ static int putChar(long a, char x) {
+ if (unalignedAccess || isAddressAligned(a, Character.BYTES)) {
+ putChar_(a, x);
+ return Character.BYTES;
+ }
+ putCharBigEndianUnaligned(a, x);
+ return Character.BYTES;
+ }
+
+ static int putShort(long a, short x) {
+ if (unalignedAccess || isAddressAligned(a, Short.BYTES)) {
+ putShort_(a, x);
+ return Short.BYTES;
+ }
+ putShortBigEndianUnaligned(a, x);
+ return Short.BYTES;
+ }
+
+ static int putInt(long a, int x) {
+ if (unalignedAccess || isAddressAligned(a, Integer.BYTES)) {
+ putInt_(a, x);
+ return Integer.BYTES;
+ }
+ putIntBigEndianUnaligned(a, x);
+ return Integer.BYTES;
+ }
+
+ static int putLong(long a, long x) {
+ if (unalignedAccess || isAddressAligned(a, Long.BYTES)) {
+ putLong_(a, x);
+ return Long.BYTES;
+ }
+ putLongBigEndianUnaligned(a, x);
+ return Long.BYTES;
+ }
+
+ static int putFloat(long a, float x) {
+ if (unalignedAccess || isAddressAligned(a, Float.BYTES)) {
+ putFloat_(a, x);
+ return Float.BYTES;
+ }
+ putFloatBigEndianUnaligned(a, x);
+ return Float.BYTES;
+ }
+
+ static int putDouble(long a, double x) {
+ if (unalignedAccess || isAddressAligned(a, Double.BYTES)) {
+ putDouble_(a, x);
+ return Double.BYTES;
+ }
+ putDoubleBigEndianUnaligned(a, x);
+ return Double.BYTES;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/ChunkInputStream.java b/src/share/classes/jdk/jfr/internal/ChunkInputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a27170c6b91ef445e3e675d17191981931c1008
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/ChunkInputStream.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+final class ChunkInputStream extends InputStream {
+ private final Iterator chunks;
+ private RepositoryChunk currentChunk;
+ private InputStream stream;
+
+ ChunkInputStream(List chunks) throws IOException {
+ List l = new ArrayList<>(chunks.size());
+ for (RepositoryChunk c : chunks) {
+ c.use(); // keep alive while we're reading.
+ l.add(c);
+ }
+
+ this.chunks = l.iterator();
+ nextStream();
+ }
+
+ @Override
+ public int available() throws IOException {
+ if (stream != null) {
+ return stream.available();
+ }
+ return 0;
+ }
+
+ private boolean nextStream() throws IOException {
+ if (!nextChunk()) {
+ return false;
+ }
+
+ stream = new BufferedInputStream(SecuritySupport.newFileInputStream(currentChunk.getFile()));
+ return true;
+ }
+
+ private boolean nextChunk() {
+ if (!chunks.hasNext()) {
+ return false;
+ }
+ currentChunk = chunks.next();
+ return true;
+ }
+
+ @Override
+ public int read() throws IOException {
+ while (true) {
+ if (stream != null) {
+ int r = stream.read();
+ if (r != -1) {
+ return r;
+ }
+ stream.close();
+ currentChunk.release();
+ stream = null;
+ currentChunk = null;
+ }
+ if (!nextStream()) {
+ return -1;
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (stream != null) {
+ stream.close();
+ stream = null;
+ }
+ while (currentChunk != null) {
+ currentChunk.release();
+ currentChunk = null;
+ if (!nextChunk()) {
+ return;
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected void finalize() throws Throwable {
+ super.finalize();
+ close();
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/ChunksChannel.java b/src/share/classes/jdk/jfr/internal/ChunksChannel.java
new file mode 100644
index 0000000000000000000000000000000000000000..1139536c93876f8fa49ed46a625e2b07d057b49a
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/ChunksChannel.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+final class ChunksChannel implements ReadableByteChannel {
+ private final Iterator chunks;
+ private RepositoryChunk current;
+ private ReadableByteChannel channel;
+
+ public ChunksChannel(List chunks) throws IOException {
+ if (chunks.isEmpty()) {
+ throw new FileNotFoundException("No chunks");
+ }
+ List l = new ArrayList<>(chunks.size());
+ for (RepositoryChunk c : chunks) {
+ c.use(); // keep alive while we're reading.
+ l.add(c);
+ }
+ this.chunks = l.iterator();
+ nextChannel();
+ }
+
+ private boolean nextChunk() {
+ if (!chunks.hasNext()) {
+ return false;
+ }
+ current = chunks.next();
+ return true;
+ }
+
+ private boolean nextChannel() throws IOException {
+ if (!nextChunk()) {
+ return false;
+ }
+
+ channel = current.newChannel();
+ return true;
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ for (;;) {
+ if (channel != null) {
+ assert current != null;
+ int r = channel.read(dst);
+ if (r != -1) {
+ return r;
+ }
+ channel.close();
+ current.release();
+ channel = null;
+ current = null;
+ }
+ if (!nextChannel()) {
+ return -1;
+ }
+ }
+ }
+
+ public long transferTo(FileChannel out) throws IOException {
+ long pos = 0;
+ for (;;) {
+ if (channel != null) {
+ assert current != null;
+
+ long rem = current.getSize();
+
+ while (rem > 0) {
+ long n = Math.min(rem, 1024 * 1024);
+ long w = out.transferFrom(channel, pos, n);
+ pos += w;
+ rem -= w;
+ }
+
+ channel.close();
+ current.release();
+
+ channel = null;
+ current = null;
+ }
+ if (!nextChannel()) {
+ return pos;
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (channel != null) {
+ channel.close();
+ channel = null;
+ }
+ while (current != null) {
+ current.release();
+ current = null;
+ if (!nextChunk()) {
+ return;
+ }
+ }
+ }
+
+ @Override
+ public boolean isOpen() {
+ return channel != null;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected void finalize() throws Throwable {
+ super.finalize();
+ close();
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/Control.java b/src/share/classes/jdk/jfr/internal/Control.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b24aec0bbf2ed277ff0d9028e7250a30e322b0c
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/Control.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+// User must never be able to subclass directly.
+//
+// Never put Control or Setting Control in a collections
+// so overridable versions of hashCode or equals are
+// executed in the wrong context. TODO: wrap this class
+// in SsecureControl directly when it is instantiated and
+// forward calls using AccessControlContext
+abstract public class Control {
+ private final AccessControlContext context;
+ private final static int CACHE_SIZE = 5;
+ private final Set>[] cachedUnions = new HashSet>[CACHE_SIZE];
+ private final String[] cachedValues = new String[CACHE_SIZE];
+ private String defaultValue;
+ private String lastValue;
+
+ // called by exposed subclass in external API
+ public Control(AccessControlContext acc) {
+ Objects.requireNonNull(acc);
+ this.context = acc;
+
+ }
+
+ // only to be called by trusted VM code
+ public Control(String defaultValue) {
+ this.defaultValue = defaultValue;
+ this.context = null;
+ }
+
+ // For user code to override, must never be called from jdk.jfr.internal
+ // for user defined settings
+ public abstract String combine(Set values);
+
+ // For user code to override, must never be called from jdk.jfr.internal
+ // for user defined settings
+ public abstract void setValue(String value);
+
+ // For user code to override, must never be called from jdk.jfr.internal
+ // for user defined settings
+ public abstract String getValue();
+
+ // Package private, user code should not have access to this method
+ final void apply(Set values) {
+ setValueSafe(findCombineSafe(values));
+ }
+
+ // Package private, user code should not have access to this method.
+ // Only called during event registration
+ final void setDefault() {
+ if (defaultValue == null) {
+ defaultValue = getValueSafe();
+ }
+ apply(defaultValue);
+ }
+
+ final String getValueSafe() {
+ if (context == null) {
+ // VM events requires no access control context
+ return getValue();
+ } else {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public String run() {
+ try {
+ return getValue();
+ } catch (Throwable t) {
+ // Prevent malicious user to propagate exception callback in the wrong context
+ Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when trying to get value for " + getClass());
+ }
+ return defaultValue != null ? defaultValue : ""; // Need to return something
+ }
+ }, context);
+ }
+ }
+
+ private void apply(String value) {
+ if (lastValue != null && Objects.equals(value, lastValue)) {
+ return;
+ }
+ setValueSafe(value);
+ }
+
+ final void setValueSafe(String value) {
+ if (context == null) {
+ // VM events requires no access control context
+ try {
+ setValue(value);
+ } catch (Throwable t) {
+ Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass());
+ }
+ } else {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public Void run() {
+ try {
+ setValue(value);
+ } catch (Throwable t) {
+ // Prevent malicious user to propagate exception callback in the wrong context
+ Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass());
+ }
+ return null;
+ }
+ }, context);
+ }
+ lastValue = value;
+ }
+
+
+ private String combineSafe(Set values) {
+ if (context == null) {
+ // VM events requires no access control context
+ return combine(values);
+ }
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public String run() {
+ try {
+ combine(Collections.unmodifiableSet(values));
+ } catch (Throwable t) {
+ // Prevent malicious user to propagate exception callback in the wrong context
+ Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when combining " + values + " for " + getClass());
+ }
+ return null;
+ }
+ }, context);
+ }
+
+ private final String findCombineSafe(Set values) {
+ if (values.size() == 1) {
+ return values.iterator().next();
+ }
+ for (int i = 0; i < CACHE_SIZE; i++) {
+ if (Objects.equals(cachedUnions[i], values)) {
+ return cachedValues[i];
+ }
+ }
+ String result = combineSafe(values);
+ for (int i = 0; i < CACHE_SIZE - 1; i++) {
+ cachedUnions[i + 1] = cachedUnions[i];
+ cachedValues[i + 1] = cachedValues[i];
+ }
+ cachedValues[0] = result;
+ cachedUnions[0] = values;
+ return result;
+ }
+
+
+ // package private, user code should not have access to this method
+ final String getDefaultValue() {
+ return defaultValue;
+ }
+
+ // package private, user code should not have access to this method
+ final String getLastValue() {
+ return lastValue;
+ }
+
+ // Precaution to prevent a malicious user from instantiating instances
+ // of a control where the context has not been set up.
+ @Override
+ public final Object clone() throws java.lang.CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+
+ private final void writeObject(ObjectOutputStream out) throws IOException {
+ throw new IOException("Object cannot be serialized");
+ }
+
+ private final void readObject(ObjectInputStream in) throws IOException {
+ throw new IOException("Class cannot be deserialized");
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/Cutoff.java b/src/share/classes/jdk/jfr/internal/Cutoff.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b3bcc7f164244dd58cf0916abc3a1aeaddb4bd1
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/Cutoff.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import jdk.jfr.MetadataDefinition;
+
+/**
+ * Event annotation, determines the cutoff above which an event should not be
+ * recorded, i.e. {@code "20 ms"}.
+ *
+ * This settings is only supported for JVM events,
+ *
+ * @since 9
+ */
+@MetadataDefinition
+@Target({ ElementType.TYPE })
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Cutoff {
+ /**
+ * Settings name {@code "cutoff"} for configuring event cutoffs.
+ */
+ public final static String NAME = "cutoff";
+ public final static String INIFITY = "infinity";
+
+ /**
+ * Cutoff, for example {@code "20 ms"}.
+ *
+ * String representation of a positive {@code Long} value followed by an empty
+ * space and one of the following units
+ *
+ * {@code "ns"} (nanoseconds)
+ * {@code "us"} (microseconds)
+ * {@code "ms"} (milliseconds)
+ * {@code "s"} (seconds)
+ * {@code "m"} (minutes)
+ * {@code "h"} (hours)
+ * {@code "d"} (days)
+ *
+ * Example values, {@code "0 ns"}, {@code "10 ms"} and {@code "1 s"}. If the
+ * events has an infinite timespan, the text {@code"infinity"} should be used.
+ *
+ * @return the threshold, default {@code "0 ns"} not {@code null}
+ */
+ String value() default "inifity";
+}
diff --git a/src/share/classes/jdk/jfr/internal/EventClassBuilder.java b/src/share/classes/jdk/jfr/internal/EventClassBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..d20b773465a3bf461cb1eec0e5d973d72c6773b4
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/EventClassBuilder.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;
+import jdk.internal.org.objectweb.asm.commons.Method;
+import jdk.jfr.AnnotationElement;
+import jdk.jfr.Event;
+import jdk.jfr.ValueDescriptor;
+
+
+// Helper class for building dynamic events
+public final class EventClassBuilder {
+
+ private static final Type TYPE_EVENT = Type.getType(Event.class);
+ private static final Type TYPE_IOBE = Type.getType(IndexOutOfBoundsException.class);
+ private static final Method DEFAULT_CONSTRUCTOR = Method.getMethod("void ()");
+ private static final Method SET_METHOD = Method.getMethod("void set (int, java.lang.Object)");
+ private static final AtomicLong idCounter = new AtomicLong();
+ private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ private final String fullClassName;
+ private final Type type;
+ private final List fields;
+ private final List annotationElements;
+
+ public EventClassBuilder(List annotationElements, List fields) {
+ this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet();
+ this.type = Type.getType(fullClassName.replace(".", "/"));
+ this.fields = fields;
+ this.annotationElements = annotationElements;
+ }
+
+ public Class extends Event> build() {
+ buildClassInfo();
+ buildConstructor();
+ buildFields();
+ buildSetMethod();
+ endClass();
+ byte[] bytes = classWriter.toByteArray();
+ ASMToolkit.logASM(fullClassName, bytes);
+ return SecuritySupport.defineClass(type.getInternalName(), bytes, Event.class.getClassLoader()).asSubclass(Event.class);
+ }
+
+ private void endClass() {
+ classWriter.visitEnd();
+ }
+
+ private void buildSetMethod() {
+ GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, SET_METHOD, null, null, classWriter);
+ int index = 0;
+ for (ValueDescriptor v : fields) {
+ ga.loadArg(0);
+ ga.visitLdcInsn(index);
+ Label notEqual = new Label();
+ ga.ifICmp(GeneratorAdapter.NE, notEqual);
+ ga.loadThis();
+ ga.loadArg(1);
+ Type fieldType = ASMToolkit.toType(v);
+ ga.unbox(ASMToolkit.toType(v));
+ ga.putField(type, v.getName(), fieldType);
+ ga.visitInsn(Opcodes.RETURN);
+ ga.visitLabel(notEqual);
+ index++;
+ }
+ ga.throwException(TYPE_IOBE, "Index must between 0 and " + fields.size());
+ ga.endMethod();
+ }
+
+ private void buildConstructor() {
+ MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), null, null);
+ mv.visitIntInsn(Opcodes.ALOAD, 0);
+ ASMToolkit.invokeSpecial(mv, TYPE_EVENT.getInternalName(), DEFAULT_CONSTRUCTOR);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ }
+
+ private void buildClassInfo() {
+ String internalSuperName = ASMToolkit.getInternalName(Event.class.getName());
+ String internalClassName = type.getInternalName();
+ classWriter.visit(52, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
+
+ for (AnnotationElement a : annotationElements) {
+ String descriptor = ASMToolkit.getDescriptor(a.getTypeName());
+ AnnotationVisitor av = classWriter.visitAnnotation(descriptor, true);
+ for (ValueDescriptor v : a.getValueDescriptors()) {
+ Object value = a.getValue(v.getName());
+ String name = v.getName();
+ if (v.isArray()) {
+ AnnotationVisitor arrayVisitor = av.visitArray(name);
+ Object[] array = (Object[]) value;
+ for (int i = 0; i < array.length; i++) {
+ arrayVisitor.visit(null, array[i]);
+ }
+ arrayVisitor.visitEnd();
+ } else {
+ av.visit(name, value);
+ }
+ }
+ av.visitEnd();
+ }
+ }
+
+ private void buildFields() {
+ for (ValueDescriptor v : fields) {
+ String internal = ASMToolkit.getDescriptor(v.getTypeName());
+ classWriter.visitField(Opcodes.ACC_PRIVATE, v.getName(), internal, null, null);
+ // No need to store annotations on field since they will be replaced anyway.
+ }
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/EventControl.java b/src/share/classes/jdk/jfr/internal/EventControl.java
new file mode 100644
index 0000000000000000000000000000000000000000..33a46c3eaf23bd0ec18d5fb47f270c3d025a86ca
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/EventControl.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import jdk.jfr.AnnotationElement;
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.Period;
+import jdk.jfr.SettingControl;
+import jdk.jfr.SettingDefinition;
+import jdk.jfr.StackTrace;
+import jdk.jfr.Threshold;
+import jdk.jfr.events.ActiveSettingEvent;
+import jdk.jfr.internal.EventInstrumentation.SettingInfo;
+import jdk.jfr.internal.settings.CutoffSetting;
+import jdk.jfr.internal.settings.EnabledSetting;
+import jdk.jfr.internal.settings.PeriodSetting;
+import jdk.jfr.internal.settings.StackTraceSetting;
+import jdk.jfr.internal.settings.ThresholdSetting;
+
+// This class can't have a hard reference from PlatformEventType, since it
+// holds SettingControl instances that need to be released
+// when a class is unloaded (to avoid memory leaks).
+public final class EventControl {
+
+ static final String FIELD_SETTING_PREFIX = "setting";
+ private static final Type TYPE_ENABLED = TypeLibrary.createType(EnabledSetting.class);
+ private static final Type TYPE_THRESHOLD = TypeLibrary.createType(ThresholdSetting.class);
+ private static final Type TYPE_STACK_TRACE = TypeLibrary.createType(StackTraceSetting.class);
+ private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class);
+ private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class);
+
+ private final List settingInfos = new ArrayList<>();
+ private final Map eventControls = new HashMap<>(5);
+ private final PlatformEventType type;
+ private final String idName;
+
+ EventControl(PlatformEventType eventType) {
+ eventControls.put(Enabled.NAME, defineEnabled(eventType));
+ if (eventType.hasDuration()) {
+ eventControls.put(Threshold.NAME, defineThreshold(eventType));
+ }
+ if (eventType.hasStackTrace()) {
+ eventControls.put(StackTrace.NAME, defineStackTrace(eventType));
+ }
+ if (eventType.hasPeriod()) {
+ eventControls.put(Period.NAME, definePeriod(eventType));
+ }
+ if (eventType.hasCutoff()) {
+ eventControls.put(Cutoff.NAME, defineCutoff(eventType));
+ }
+
+ ArrayList aes = new ArrayList<>(eventType.getAnnotationElements());
+ remove(eventType, aes, Threshold.class);
+ remove(eventType, aes, Period.class);
+ remove(eventType, aes, Enabled.class);
+ remove(eventType, aes, StackTrace.class);
+ remove(eventType, aes, Cutoff.class);
+ aes.trimToSize();
+ eventType.setAnnotations(aes);
+ this.type = eventType;
+ this.idName = String.valueOf(eventType.getId());
+ }
+
+ static void remove(PlatformEventType type, List aes, Class extends java.lang.annotation.Annotation> clazz) {
+ long id = Type.getTypeId(clazz);
+ for (AnnotationElement a : type.getAnnotationElements()) {
+ if (a.getTypeId() == id && a.getTypeName().equals(clazz.getName())) {
+ aes.remove(a);
+ }
+ }
+ }
+
+ EventControl(PlatformEventType es, Class extends Event> eventClass) {
+ this(es);
+ defineSettings(eventClass);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void defineSettings(Class> eventClass) {
+ // Iterate up the class hierarchy and let
+ // subclasses shadow base classes.
+ boolean allowPrivateMethod = true;
+ while (eventClass != null) {
+ for (Method m : eventClass.getDeclaredMethods()) {
+ boolean isPrivate = Modifier.isPrivate(m.getModifiers());
+ if (m.getReturnType() == Boolean.TYPE && m.getParameterCount() == 1 && (!isPrivate || allowPrivateMethod)) {
+ SettingDefinition se = m.getDeclaredAnnotation(SettingDefinition.class);
+ if (se != null) {
+ Class> settingClass = m.getParameters()[0].getType();
+ if (!Modifier.isAbstract(settingClass.getModifiers()) && SettingControl.class.isAssignableFrom(settingClass)) {
+ String name = m.getName();
+ Name n = m.getAnnotation(Name.class);
+ if (n != null) {
+ name = n.value();
+ }
+ if (!eventControls.containsKey(name)) {
+ defineSetting((Class extends SettingControl>) settingClass, m, type, name);
+ }
+ }
+ }
+ }
+ }
+ eventClass = eventClass.getSuperclass();
+ allowPrivateMethod = false;
+ }
+ }
+
+ private void defineSetting(Class extends SettingControl> settingsClass, Method method, PlatformEventType eventType, String settingName) {
+ try {
+ int index = settingInfos.size();
+ SettingInfo si = new SettingInfo(FIELD_SETTING_PREFIX + index, index);
+ si.settingControl = instantiateSettingControl(settingsClass);
+ Control c = si.settingControl;
+ c.setDefault();
+ String defaultValue = c.getValueSafe();
+ if (defaultValue != null) {
+ Type settingType = TypeLibrary.createType(settingsClass);
+ ArrayList aes = new ArrayList<>();
+ for (Annotation a : method.getDeclaredAnnotations()) {
+ AnnotationElement ae = TypeLibrary.createAnnotation(a);
+ if (ae != null) {
+ aes.add(ae);
+ }
+ }
+ aes.trimToSize();
+ eventControls.put(settingName, si.settingControl);
+ eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, settingName, defaultValue, aes));
+ settingInfos.add(si);
+ }
+ } catch (InstantiationException e) {
+ // Programming error by user, fail fast
+ throw new InstantiationError("Could not instantiate setting " + settingsClass.getName() + " for event " + eventType.getLogName() + ". " + e.getMessage());
+ } catch (IllegalAccessException e) {
+ // Programming error by user, fail fast
+ throw new IllegalAccessError("Could not access setting " + settingsClass.getName() + " for event " + eventType.getLogName() + ". " + e.getMessage());
+ }
+ }
+
+ private SettingControl instantiateSettingControl(Class extends SettingControl> settingControlClass) throws IllegalAccessException, InstantiationException {
+ SecuritySupport.makeVisibleToJFR(settingControlClass);
+ final Constructor> cc;
+ try {
+ cc = settingControlClass.getDeclaredConstructors()[0];
+ } catch (Exception e) {
+ throw (Error) new InternalError("Could not get constructor for " + settingControlClass.getName()).initCause(e);
+ }
+ SecuritySupport.setAccessible(cc);
+ try {
+ return (SettingControl) cc.newInstance();
+ } catch (IllegalArgumentException | InvocationTargetException e) {
+ throw (Error) new InternalError("Could not instantiate setting for class " + settingControlClass.getName());
+ }
+ }
+
+ private static Control defineEnabled(PlatformEventType type) {
+ Enabled enabled = type.getAnnotation(Enabled.class);
+ // Java events are enabled by default,
+ // JVM events are not, maybe they should be? Would lower learning curve
+ // there too.
+ String def = type.isJVM() ? "false" : "true";
+ if (enabled != null) {
+ def = Boolean.toString(enabled.value());
+ }
+ type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_ENABLED, Enabled.NAME, def, Collections.emptyList()));
+ return new EnabledSetting(type, def);
+ }
+
+ private static Control defineThreshold(PlatformEventType type) {
+ Threshold threshold = type.getAnnotation(Threshold.class);
+ String def = "0 ns";
+ if (threshold != null) {
+ def = threshold.value();
+ }
+ type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THRESHOLD, Threshold.NAME, def, Collections.emptyList()));
+ return new ThresholdSetting(type, def);
+ }
+
+ private static Control defineStackTrace(PlatformEventType type) {
+ StackTrace stackTrace = type.getAnnotation(StackTrace.class);
+ String def = "true";
+ if (stackTrace != null) {
+ def = Boolean.toString(stackTrace.value());
+ }
+ type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_STACK_TRACE, StackTrace.NAME, def, Collections.emptyList()));
+ return new StackTraceSetting(type, def);
+ }
+
+ private static Control defineCutoff(PlatformEventType type) {
+ Cutoff cutoff = type.getAnnotation(Cutoff.class);
+ String def = Cutoff.INIFITY;
+ if (cutoff != null) {
+ def = cutoff.value();
+ }
+ type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_CUTOFF, Cutoff.NAME, def, Collections.emptyList()));
+ return new CutoffSetting(type, def);
+ }
+
+
+ private static Control definePeriod(PlatformEventType type) {
+ Period period = type.getAnnotation(Period.class);
+ String def = "everyChunk";
+ if (period != null) {
+ def = period.value();
+ }
+ type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_PERIOD, PeriodSetting.NAME, def, Collections.emptyList()));
+ return new PeriodSetting(type, def);
+ }
+
+ void disable() {
+ for (Control c : eventControls.values()) {
+ if (c instanceof EnabledSetting) {
+ c.setValueSafe("false");
+ return;
+ }
+ }
+ }
+
+ void writeActiveSettingEvent() {
+ if (!type.isRegistered()) {
+ return;
+ }
+ for (Map.Entry entry : eventControls.entrySet()) {
+ Control c = entry.getValue();
+ if (Utils.isSettingVisible(c, type.hasEventHook())) {
+ String value = c.getLastValue();
+ if (value == null) {
+ value = c.getDefaultValue();
+ }
+ ActiveSettingEvent ase = new ActiveSettingEvent();
+ ase.id = type.getId();
+ ase.name = entry.getKey();
+ ase.value = value;
+ ase.commit();
+ }
+ }
+ }
+
+ public Set> getEntries() {
+ return eventControls.entrySet();
+ }
+
+ public PlatformEventType getEventType() {
+ return type;
+ }
+
+ public String getSettingsId() {
+ return idName;
+ }
+
+ public List getSettingInfos() {
+ return settingInfos;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/EventHandlerCreator.java b/src/share/classes/jdk/jfr/internal/EventHandlerCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d5fe27ddbc3690cee58a0fcfb25a3f48caf06e9
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/EventHandlerCreator.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.commons.Method;
+import jdk.jfr.Event;
+import jdk.jfr.EventType;
+import jdk.jfr.SettingControl;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.EventInstrumentation.FieldInfo;
+import jdk.jfr.internal.EventInstrumentation.SettingInfo;
+import jdk.jfr.internal.handlers.EventHandler;
+
+final class EventHandlerCreator {
+ // TODO:
+ // How can we find out class version without loading a
+ // class as resource in a privileged block and use ASM to inspect
+ // the contents. Using '52' even though we know later versions
+ // are available. The reason for this is compatibility aspects
+ // with for example WLS.
+ private static final int CLASS_VERSION = 52;
+
+ // This is needed so a new EventHandler is automatically generated in MetadataRespoistory
+ // if a user Event class is loaded using APPCDS/CDS.
+ private static final String SUFFIX = "_" + System.currentTimeMillis() + "-" + JVM.getJVM().getPid();
+
+ private static final String FIELD_EVENT_TYPE = "platformEventType";
+ private static final String FIELD_PREFIX_STRING_POOL = "stringPool";
+
+ private final static Type TYPE_STRING_POOL = Type.getType(StringPool.class);
+ private final static Type TYPE_EVENT_WRITER = Type.getType(EventWriter.class);
+ private final static Type TYPE_PLATFORM_EVENT_TYPE = Type.getType(PlatformEventType.class);
+ private final static Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class);
+ private final static Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
+ private final static Type TYPE_EVENT_TYPE = Type.getType(EventType.class);
+ private final static Type TYPE_EVENT_CONTROL = Type.getType(EventControl.class);
+ private final static String DESCRIPTOR_EVENT_HANDLER = "(" + Type.BOOLEAN_TYPE.getDescriptor() + TYPE_EVENT_TYPE.getDescriptor() + TYPE_EVENT_CONTROL.getDescriptor() + ")V";
+ private final static Method METHOD_GET_EVENT_WRITER = new Method("getEventWriter", "()" + TYPE_EVENT_WRITER.getDescriptor());
+ private final static Method METHOD_EVENT_HANDLER_CONSTRUCTOR = new Method("", DESCRIPTOR_EVENT_HANDLER);
+ private final static Method METHOD_RESET = new Method("reset", "()V");
+
+ private final ClassWriter classWriter;
+ private final String className;
+ private final String internalClassName;
+ private final List settingInfos;
+ private final List fields;
+
+ public EventHandlerCreator(long id, List settingInfos, List fields) {
+ this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ this.className = makeEventHandlerName(id);
+ this.internalClassName = ASMToolkit.getInternalName(className);
+ this.settingInfos = settingInfos;
+ this.fields = fields;
+ }
+
+ public static String makeEventHandlerName(long id) {
+ return EventHandler.class.getName() + id + SUFFIX;
+ }
+
+ public EventHandlerCreator(long id, List settingInfos, EventType type, Class extends Event> eventClass) {
+ this(id, settingInfos, createFieldInfos(eventClass, type));
+ }
+
+ private static List createFieldInfos(Class extends Event> eventClass, EventType type) throws Error {
+ List fieldInfos = new ArrayList<>();
+ for (ValueDescriptor v : type.getFields()) {
+ // Only value descriptors that are not fields on the event class.
+ if (v != TypeLibrary.STACK_TRACE_FIELD && v != TypeLibrary.THREAD_FIELD) {
+ String fieldName = PrivateAccess.getInstance().getFieldName(v);
+ String fieldDescriptor = ASMToolkit.getDescriptor(v.getTypeName());
+ Class> c = eventClass;
+ String internalName = null;
+ while (c != Event.class) {
+ try {
+ Field field = c.getDeclaredField(fieldName);
+ if (c == eventClass || !Modifier.isPrivate(field.getModifiers())) {
+ internalName = ASMToolkit.getInternalName(c.getName());
+ break;
+ }
+ } catch (NoSuchFieldException | SecurityException e) {
+ // ignore
+ }
+ c = c.getSuperclass();
+ }
+ if (internalName != null) {
+ fieldInfos.add(new FieldInfo(fieldName, fieldDescriptor, internalName));
+ } else {
+ throw new InternalError("Could not locate field " + fieldName + " for event type" + type.getName());
+ }
+ }
+ }
+ return fieldInfos;
+ }
+
+ public Class extends EventHandler> makeEventHandlerClass() {
+ buildClassInfo();
+ buildConstructor();
+ buildWriteMethod();
+ byte[] bytes = classWriter.toByteArray();
+ ASMToolkit.logASM(className, bytes);
+ return SecuritySupport.defineClass(className, bytes, Event.class.getClassLoader()).asSubclass(EventHandler.class);
+ }
+
+ public static EventHandler instantiateEventHandler(Class extends EventHandler> handlerClass, boolean registered, EventType eventType, EventControl eventControl) throws Error {
+ final Constructor> cc;
+ try {
+ cc = handlerClass.getDeclaredConstructors()[0];
+ } catch (Exception e) {
+ throw (Error) new InternalError("Could not get handler constructor for " + eventType.getName()).initCause(e);
+ }
+ // Users should not be allowed to create instances of the event handler
+ // so we need to unlock it here.
+ SecuritySupport.setAccessible(cc);
+ try {
+ List settingInfos = eventControl.getSettingInfos();
+ Object[] arguments = new Object[3 + settingInfos.size()];
+ arguments[0] = registered;
+ arguments[1] = eventType;
+ arguments[2] = eventControl;
+ for (SettingInfo si : settingInfos) {
+ arguments[si.index + 3] = si.settingControl;
+ }
+ return (EventHandler) cc.newInstance(arguments);
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw (Error) new InternalError("Could not instantiate event handler for " + eventType.getName() + ". " + e.getMessage()).initCause(e);
+ }
+ }
+
+ private void buildConstructor() {
+ MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PRIVATE, METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), makeConstructorDescriptor(settingInfos), null, null);
+ mv.visitVarInsn(Opcodes.ALOAD, 0); // this
+ mv.visitVarInsn(Opcodes.ILOAD, 1); // registered
+ mv.visitVarInsn(Opcodes.ALOAD, 2); // event type
+ mv.visitVarInsn(Opcodes.ALOAD, 3); // event control
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(EventHandler.class), METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), METHOD_EVENT_HANDLER_CONSTRUCTOR.getDescriptor(), false);
+ for (SettingInfo si : settingInfos) {
+ mv.visitVarInsn(Opcodes.ALOAD, 0); // this
+ mv.visitVarInsn(Opcodes.ALOAD, si.index + 4); // Setting Control
+ mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
+ }
+ // initialized string field writers
+ int fieldIndex = 0;
+ for (FieldInfo field : fields) {
+ if (field.isString()) {
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(EventHandler.class), "createStringFieldWriter", "()" + TYPE_STRING_POOL.getDescriptor(), false);
+ mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor());
+ }
+ fieldIndex++;
+ }
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ private void buildClassInfo() {
+ String internalSuperName = ASMToolkit.getInternalName(EventHandler.class.getName());
+ classWriter.visit(CLASS_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
+ for (SettingInfo si : settingInfos) {
+ classWriter.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor(), null, null);
+ }
+ int fieldIndex = 0;
+ for (FieldInfo field : fields) {
+ if (field.isString()) {
+ classWriter.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, FIELD_PREFIX_STRING_POOL+ fieldIndex, TYPE_STRING_POOL.getDescriptor(), null, null);
+ }
+ fieldIndex++;
+ }
+ }
+
+ private void visitMethod(final MethodVisitor mv, final int opcode, final Type type, final Method method) {
+ mv.visitMethodInsn(opcode, type.getInternalName(), method.getName(), method.getDescriptor(), false);
+ }
+
+ private void buildWriteMethod() {
+ int argIndex = 0; // // indexes the argument type array, the argument type array does not include 'this'
+ int slotIndex = 1; // indexes the proper slot in the local variable table, takes type size into account, therefore sometimes argIndex != slotIndex
+ int fieldIndex = 0;
+ Method desc = ASMToolkit.makeWriteMethod(fields);
+ Type[] argumentTypes = Type.getArgumentTypes(desc.getDescriptor());
+ MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, desc.getName(), desc.getDescriptor(), null, null);
+ mv.visitCode();
+ Label start = new Label();
+ Label endTryBlock = new Label();
+ Label exceptionHandler = new Label();
+ mv.visitTryCatchBlock(start, endTryBlock, exceptionHandler, "java/lang/Throwable");
+ mv.visitLabel(start);
+ visitMethod(mv, Opcodes.INVOKESTATIC, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
+ // stack: [BW]
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [BW], [BW]
+ // write begin event
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ // stack: [BW], [BW], [this]
+ mv.visitFieldInsn(Opcodes.GETFIELD, TYPE_EVENT_HANDLER.getInternalName(), FIELD_EVENT_TYPE, TYPE_PLATFORM_EVENT_TYPE.getDescriptor());
+ // stack: [BW], [BW], [BS]
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.BEGIN_EVENT.asASM());
+ // stack: [BW], [integer]
+ Label recursive = new Label();
+ mv.visitJumpInsn(Opcodes.IFEQ, recursive);
+ // stack: [BW]
+ // write startTime
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [BW], [BW]
+ mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
+ // stack: [BW], [BW], [long]
+ slotIndex += argumentTypes[argIndex++].getSize();
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM());
+ // stack: [BW]
+ fieldIndex++;
+ // write duration
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [BW], [BW]
+ mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
+ // stack: [BW], [BW], [long]
+ slotIndex += argumentTypes[argIndex++].getSize();
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_LONG.asASM());
+ // stack: [BW]
+ fieldIndex++;
+ // write eventThread
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [BW], [BW]
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_EVENT_THREAD.asASM());
+ // stack: [BW]
+ // write stackTrace
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [BW], [BW]
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.PUT_STACK_TRACE.asASM());
+ // stack: [BW]
+ // write custom fields
+ while (fieldIndex < fields.size()) {
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [BW], [BW]
+ mv.visitVarInsn(argumentTypes[argIndex].getOpcode(Opcodes.ILOAD), slotIndex);
+ // stack:[BW], [BW], [field]
+ slotIndex += argumentTypes[argIndex++].getSize();
+ FieldInfo field = fields.get(fieldIndex);
+ if (field.isString()) {
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ // stack:[BW], [BW], [field], [this]
+ mv.visitFieldInsn(Opcodes.GETFIELD, this.internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor());
+ // stack:[BW], [BW], [field], [string]
+ }
+ EventWriterMethod eventMethod = EventWriterMethod.lookupMethod(field);
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, eventMethod.asASM());
+ // stack: [BW]
+ fieldIndex++;
+ }
+ // stack: [BW]
+ // write end event (writer already on stack)
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, EventWriterMethod.END_EVENT.asASM());
+ // stack [integer]
+ // notified -> restart event write attempt
+ mv.visitJumpInsn(Opcodes.IFEQ, start);
+ // stack:
+ mv.visitLabel(endTryBlock);
+ Label end = new Label();
+ mv.visitJumpInsn(Opcodes.GOTO, end);
+ mv.visitLabel(exceptionHandler);
+ // stack: [ex]
+ mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
+ visitMethod(mv, Opcodes.INVOKESTATIC, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
+ // stack: [ex] [BW]
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [ex] [BW] [BW]
+ Label rethrow = new Label();
+ mv.visitJumpInsn(Opcodes.IFNULL, rethrow);
+ // stack: [ex] [BW]
+ mv.visitInsn(Opcodes.DUP);
+ // stack: [ex] [BW] [BW]
+ visitMethod(mv, Opcodes.INVOKEVIRTUAL, TYPE_EVENT_WRITER, METHOD_RESET);
+ mv.visitLabel(rethrow);
+ // stack:[ex] [BW]
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 2, new Object[] {"java/lang/Throwable", TYPE_EVENT_WRITER.getInternalName()});
+ mv.visitInsn(Opcodes.POP);
+ // stack:[ex]
+ mv.visitInsn(Opcodes.ATHROW);
+ mv.visitLabel(recursive);
+ // stack: [BW]
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] { TYPE_EVENT_WRITER.getInternalName()} );
+ mv.visitInsn(Opcodes.POP);
+ mv.visitLabel(end);
+ // stack:
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ private static String makeConstructorDescriptor(List settingsInfos) {
+ StringJoiner constructordescriptor = new StringJoiner("", "(", ")V");
+ constructordescriptor.add(Type.BOOLEAN_TYPE.getDescriptor());
+ constructordescriptor.add(Type.getType(EventType.class).getDescriptor());
+ constructordescriptor.add(Type.getType(EventControl.class).getDescriptor());
+ for (int i = 0; i < settingsInfos.size(); i++) {
+ constructordescriptor.add(TYPE_SETTING_CONTROL.getDescriptor());
+ }
+ return constructordescriptor.toString();
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/EventInstrumentation.java b/src/share/classes/jdk/jfr/internal/EventInstrumentation.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b3654587169d7848b5a1a24b80d218a9f31eaa7
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/EventInstrumentation.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.commons.Method;
+import jdk.internal.org.objectweb.asm.tree.AnnotationNode;
+import jdk.internal.org.objectweb.asm.tree.ClassNode;
+import jdk.internal.org.objectweb.asm.tree.FieldNode;
+import jdk.internal.org.objectweb.asm.tree.MethodNode;
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.Registered;
+import jdk.jfr.SettingControl;
+import jdk.jfr.SettingDefinition;
+import jdk.jfr.internal.handlers.EventHandler;
+
+/**
+ * Class responsible for adding instrumentation to a subclass of {@link Event}.
+ *
+ */
+public final class EventInstrumentation {
+ static final class SettingInfo {
+ private String methodName;
+ private String internalSettingName;
+ private String settingDescriptor;
+ final String fieldName;
+ final int index;
+ // Used when instantiating Setting
+ SettingControl settingControl;
+
+ public SettingInfo(String fieldName, int index) {
+ this.fieldName = fieldName;
+ this.index = index;
+ }
+ }
+
+ static final class FieldInfo {
+ private final static Type STRING = Type.getType(String.class);
+ final String fieldName;
+ final String fieldDescriptor;
+ final String internalClassName;
+
+ public FieldInfo(String fieldName, String fieldDescriptor, String internalClassName) {
+ this.fieldName = fieldName;
+ this.fieldDescriptor = fieldDescriptor;
+ this.internalClassName = internalClassName;
+ }
+
+ public boolean isString() {
+ return STRING.getDescriptor().equals(fieldDescriptor);
+ }
+ }
+
+ public static final String FIELD_EVENT_THREAD = "eventThread";
+ public static final String FIELD_STACK_TRACE = "stackTrace";
+ public static final String FIELD_DURATION = "duration";
+
+ static final String FIELD_EVENT_HANDLER = "eventHandler";
+ static final String FIELD_START_TIME = "startTime";
+
+ private static final Type ANNOTATION_TYPE_NAME = Type.getType(Name.class);
+ private static final Type ANNOTATION_TYPE_REGISTERED = Type.getType(Registered.class);
+ private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class);
+ private static final Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class);
+ private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
+ private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]);
+ private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]);
+ private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]);
+ private static final Method METHOD_IS_ENABLED = new Method("isEnabled", Type.BOOLEAN_TYPE, new Type[0]);
+ private static final Method METHOD_TIME_STAMP = new Method("timestamp", Type.LONG_TYPE, new Type[0]);
+ private static final Method METHOD_EVENT_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[0]);
+ private static final Method METHOD_EVENT_HANDLER_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[] { Type.LONG_TYPE });
+ private static final Method METHOD_DURATION = new Method("duration", Type.LONG_TYPE, new Type[] { Type.LONG_TYPE });
+
+ private final ClassNode classNode;
+ private final List settingInfos;
+ private final List fieldInfos;;
+ private final Method writeMethod;
+ private final String eventHandlerXInternalName;
+ private final String eventName;
+ private boolean guardHandlerReference;
+ private Class> superClass;
+
+ EventInstrumentation(Class> superClass, byte[] bytes, long id) {
+ this.superClass = superClass;
+ this.classNode = createClassNode(bytes);
+ this.settingInfos = buildSettingInfos(superClass, classNode);
+ this.fieldInfos = buildFieldInfos(superClass, classNode);
+ this.writeMethod = makeWriteMethod(fieldInfos);
+ this.eventHandlerXInternalName = ASMToolkit.getInternalName(EventHandlerCreator.makeEventHandlerName(id));
+ String n = annotationValue(classNode, ANNOTATION_TYPE_NAME.getDescriptor(), String.class);
+ this.eventName = n == null ? classNode.name.replace("/", ".") : n;
+
+ }
+
+ public String getClassName() {
+ return classNode.name.replace("/",".");
+ }
+
+ private ClassNode createClassNode(byte[] bytes) {
+ ClassNode classNode = new ClassNode();
+ ClassReader classReader = new ClassReader(bytes);
+ classReader.accept(classNode, 0);
+ return classNode;
+ }
+
+ boolean isRegistered() {
+ Boolean result = annotationValue(classNode, ANNOTATION_TYPE_REGISTERED.getDescriptor(), Boolean.class);
+ if (result != null) {
+ return result.booleanValue();
+ }
+ if (superClass != null) {
+ Registered r = superClass.getAnnotation(Registered.class);
+ if (r != null) {
+ return r.value();
+ }
+ }
+ return true;
+ }
+
+ boolean isEnabled() {
+ Boolean result = annotationValue(classNode, ANNOTATION_TYPE_ENABLED.getDescriptor(), Boolean.class);
+ if (result != null) {
+ return result.booleanValue();
+ }
+ if (superClass != null) {
+ Enabled e = superClass.getAnnotation(Enabled.class);
+ if (e != null) {
+ return e.value();
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static T annotationValue(ClassNode classNode, String typeDescriptor, Class> type) {
+ if (classNode.visibleAnnotations != null) {
+ for (AnnotationNode a : classNode.visibleAnnotations) {
+ if (typeDescriptor.equals(a.desc)) {
+ List values = a.values;
+ if (values != null && values.size() == 2) {
+ Object key = values.get(0);
+ Object value = values.get(1);
+ if (key instanceof String && value != null) {
+ if (type == value.getClass()) {
+ String keyName = (String) key;
+ if ("value".equals(keyName)) {
+ return (T) value;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static List buildSettingInfos(Class> superClass, ClassNode classNode) {
+ Set methodSet = new HashSet<>();
+ List settingInfos = new ArrayList<>();
+ String settingDescriptor = Type.getType(SettingDefinition.class).getDescriptor();
+ for (MethodNode m : classNode.methods) {
+ if (m.visibleAnnotations != null) {
+ for (AnnotationNode an : m.visibleAnnotations) {
+ // We can't really validate the method at this
+ // stage. We would need to check that the parameter
+ // is an instance of SettingControl.
+ if (settingDescriptor.equals(an.desc)) {
+ Type returnType = Type.getReturnType(m.desc);
+ if (returnType.equals(Type.getType(Boolean.TYPE))) {
+ Type[] args = Type.getArgumentTypes(m.desc);
+ if (args.length == 1) {
+ Type paramType = args[0];
+ String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size();
+ int index = settingInfos.size();
+ SettingInfo si = new SettingInfo(fieldName, index);
+ si.methodName = m.name;
+ si.settingDescriptor = paramType.getDescriptor();
+ si.internalSettingName = paramType.getInternalName();
+ methodSet.add(m.name);
+ settingInfos.add(si);
+ }
+ }
+ }
+ }
+ }
+ }
+ for (Class> c = superClass; c != Event.class; c = c.getSuperclass()) {
+ for (java.lang.reflect.Method method : c.getDeclaredMethods()) {
+ if (!methodSet.contains(method.getName())) {
+ // skip private method in base classes
+ if (!Modifier.isPrivate(method.getModifiers())) {
+ if (method.getReturnType().equals(Boolean.TYPE)) {
+ if (method.getParameterCount() == 1) {
+ Parameter param = method.getParameters()[0];
+ Type paramType = Type.getType(param.getType());
+ String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size();
+ int index = settingInfos.size();
+ SettingInfo si = new SettingInfo(fieldName, index);
+ si.methodName = method.getName();
+ si.settingDescriptor = paramType.getDescriptor();
+ si.internalSettingName = paramType.getInternalName();
+ methodSet.add(method.getName());
+ settingInfos.add(si);
+ }
+ }
+ }
+ }
+ }
+ }
+ return settingInfos;
+
+ }
+
+ private static List buildFieldInfos(Class> superClass, ClassNode classNode) {
+ Set fieldSet = new HashSet<>();
+ List fieldInfos = new ArrayList<>(classNode.fields.size());
+ // These two field are added by native as transient so they will be
+ // ignored by the loop below.
+ // The benefit of adding them manually is that we can
+ // control in which order they occur and we can add @Name, @Description
+ // in Java, instead of in native. It also means code for adding implicit
+ // fields for native can be reused by Java.
+ fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor(), classNode.name));
+ fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor(), classNode.name));
+ for (FieldNode field : classNode.fields) {
+ String className = Type.getType(field.desc).getClassName();
+ if (!fieldSet.contains(field.name) && isValidField(field.access, className)) {
+ FieldInfo fi = new FieldInfo(field.name, field.desc, classNode.name);
+ fieldInfos.add(fi);
+ fieldSet.add(field.name);
+ }
+ }
+ for (Class> c = superClass; c != Event.class; c = c.getSuperclass()) {
+ for (Field field : c.getDeclaredFields()) {
+ // skip private field in base classes
+ if (!Modifier.isPrivate(field.getModifiers())) {
+ if (isValidField(field.getModifiers(), field.getType().getName())) {
+ String fieldName = field.getName();
+ if (!fieldSet.contains(fieldName)) {
+ Type fieldType = Type.getType(field.getType());
+ String internalClassName = ASMToolkit.getInternalName(c.getName());
+ fieldInfos.add(new FieldInfo(fieldName, fieldType.getDescriptor(), internalClassName));
+ fieldSet.add(fieldName);
+ }
+ }
+ }
+ }
+ }
+ return fieldInfos;
+ }
+
+ public static boolean isValidField(int access, String className) {
+ if (Modifier.isTransient(access) || Modifier.isStatic(access)) {
+ return false;
+ }
+ return jdk.jfr.internal.Type.isValidJavaFieldType(className);
+ }
+
+ public byte[] buildInstrumented() {
+ makeInstrumented();
+ return toByteArray();
+ }
+
+ private byte[] toByteArray() {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ classNode.accept(cw);
+ cw.visitEnd();
+ byte[] result = cw.toByteArray();
+ Utils.writeGeneratedASM(classNode.name, result);
+ return result;
+ }
+
+ public byte[] builUninstrumented() {
+ makeUninstrumented();
+ return toByteArray();
+ }
+
+ private void makeInstrumented() {
+ // MyEvent#isEnabled()
+ updateMethod(METHOD_IS_ENABLED, methodVisitor -> {
+ Label nullLabel = new Label();
+ if (guardHandlerReference) {
+ methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
+ methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel);
+ }
+ methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
+ ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_IS_ENABLED);
+ methodVisitor.visitInsn(Opcodes.IRETURN);
+ if (guardHandlerReference) {
+ methodVisitor.visitLabel(nullLabel);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitInsn(Opcodes.ICONST_0);
+ methodVisitor.visitInsn(Opcodes.IRETURN);
+ }
+ });
+
+ // MyEvent#begin()
+ updateMethod(METHOD_BEGIN, methodVisitor -> {
+ methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
+ ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP);
+ methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ });
+
+ // MyEvent#end()
+ updateMethod(METHOD_END, methodVisitor -> {
+ methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_DURATION);
+ methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitMaxs(0, 0);
+ });
+
+ // MyEvent#commit() - Java event writer
+ updateMethod(METHOD_COMMIT, methodVisitor -> {
+ // if (!isEnable()) {
+ // return;
+ // }
+ methodVisitor.visitCode();
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
+ Label l0 = new Label();
+ methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitLabel(l0);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ // if (startTime == 0) {
+ // startTime = EventWriter.timestamp();
+ // } else {
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ methodVisitor.visitInsn(Opcodes.LCONST_0);
+ methodVisitor.visitInsn(Opcodes.LCMP);
+ Label durationalEvent = new Label();
+ methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(),
+ METHOD_TIME_STAMP.getDescriptor(), false);
+ methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ Label commit = new Label();
+ methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
+ // if (duration == 0) {
+ // duration = EventWriter.timestamp() - startTime;
+ // }
+ // }
+ methodVisitor.visitLabel(durationalEvent);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
+ methodVisitor.visitInsn(Opcodes.LCONST_0);
+ methodVisitor.visitInsn(Opcodes.LCMP);
+ methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+ methodVisitor.visitInsn(Opcodes.LSUB);
+ methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
+ methodVisitor.visitLabel(commit);
+ // if (shouldCommit()) {
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
+ Label end = new Label();
+ // eventHandler.write(...);
+ // }
+ methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
+ methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
+
+ methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
+ for (FieldInfo fi : fieldInfos) {
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
+ }
+
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
+ methodVisitor.visitLabel(end);
+ methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitEnd();
+ });
+
+ // MyEvent#shouldCommit()
+ updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
+ Label fail = new Label();
+ // if (!eventHandler.shoouldCommit(duration) goto fail;
+ methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
+ ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT);
+ methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail);
+ for (SettingInfo si : settingInfos) {
+ // if (!settingsMethod(eventHandler.settingX)) goto fail;
+ methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class));
+ methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
+ methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
+ methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName);
+ methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), si.methodName, "(" + si.settingDescriptor + ")Z", false);
+ methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail);
+ }
+ // return true
+ methodVisitor.visitInsn(Opcodes.ICONST_1);
+ methodVisitor.visitInsn(Opcodes.IRETURN);
+ // return false
+ methodVisitor.visitLabel(fail);
+ methodVisitor.visitInsn(Opcodes.ICONST_0);
+ methodVisitor.visitInsn(Opcodes.IRETURN);
+ });
+ }
+
+ private void makeUninstrumented() {
+ updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT);
+ updateExistingWithReturnFalse(METHOD_IS_ENABLED);
+ updateExistingWithEmptyVoidMethod(METHOD_COMMIT);
+ updateExistingWithEmptyVoidMethod(METHOD_BEGIN);
+ updateExistingWithEmptyVoidMethod(METHOD_END);
+ }
+
+ private final void updateExistingWithEmptyVoidMethod(Method voidMethod) {
+ updateMethod(voidMethod, methodVisitor -> {
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ });
+ }
+
+ private final void updateExistingWithReturnFalse(Method voidMethod) {
+ updateMethod(voidMethod, methodVisitor -> {
+ methodVisitor.visitInsn(Opcodes.ICONST_0);
+ methodVisitor.visitInsn(Opcodes.IRETURN);
+ });
+ }
+
+ private MethodNode getMethodNode(Method method) {
+ for (MethodNode m : classNode.methods) {
+ if (m.name.equals(method.getName()) && m.desc.equals(method.getDescriptor())) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ private final void updateMethod(Method method, Consumer code) {
+ MethodNode old = getMethodNode(method);
+ int index = classNode.methods.indexOf(old);
+ classNode.methods.remove(old);
+ MethodVisitor mv = classNode.visitMethod(old.access, old.name, old.desc, null, null);
+ mv.visitCode();
+ code.accept(mv);
+ mv.visitMaxs(0, 0);
+ MethodNode newMethod = getMethodNode(method);
+ classNode.methods.remove(newMethod);
+ classNode.methods.add(index, newMethod);
+ }
+
+ public static Method makeWriteMethod(List fields) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ for (FieldInfo v : fields) {
+ sb.append(v.fieldDescriptor);
+ }
+ sb.append(")V");
+ return new Method("write", sb.toString());
+ }
+
+ private String getInternalClassName() {
+ return classNode.name;
+ }
+
+ public List getSettingInfos() {
+ return settingInfos;
+ }
+
+ public List getFieldInfos() {
+ return fieldInfos;
+ }
+
+ public String getEventName() {
+ return eventName;
+ }
+
+ public void setGuardHandler(boolean guardHandlerReference) {
+ this.guardHandlerReference = guardHandlerReference;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/EventWriter.java b/src/share/classes/jdk/jfr/internal/EventWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef30e9411ed2c76614918986acdfa1968fcc6e35
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/EventWriter.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import sun.misc.Unsafe;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Class must reside in a package with package restriction.
+ *
+ * Users should not have direct access to underlying memory.
+ *
+ */
+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;
+ // set by the JVM, not private to avoid being optimized out
+ 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); // 1 byte
+ putUncheckedInt(length); // max 5 bytes
+ for (int i = 0; i < length; i++) {
+ putUncheckedChar(s.charAt(i)); // max 3 bytes
+ }
+ }
+ }
+
+ 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() {
+ // move currentPosition Integer.Bytes offset from start position
+ 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);
+ // retry
+ 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) {
+ // recursive write attempt
+ 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();
+ // returning false will trigger restart of the event write attempt
+ return false;
+ }
+ startPosition = currentPosition;
+ unsafe.putAddress(startPositionAddress, startPosition);
+ // the event is now committed
+ 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;
+ // event may not exceed size for a padded integer
+ maxEventSize = (1 << 28) -1;
+ }
+
+ private static int makePaddedInt(int v) {
+ // bit 0-6 + pad => bit 24 - 31
+ long b1 = (((v >>> 0) & 0x7F) | 0x80) << 24;
+
+ // bit 7-13 + pad => bit 16 - 23
+ long b2 = (((v >>> 7) & 0x7F) | 0x80) << 16;
+
+ // bit 14-20 + pad => bit 8 - 15
+ long b3 = (((v >>> 14) & 0x7F) | 0x80) << 8;
+
+ // bit 21-28 => bit 0 - 7
+ long b4 = (((v >>> 21) & 0x7F)) << 0;
+
+ return (int) (b1 + b2 + b3 + b4);
+ }
+
+ private void putUncheckedLong(long v) {
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 0-6
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 0-6
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 7-13
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 7-13
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 14-20
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 14-20
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 21-27
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 21-27
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 28-34
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 28-34
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 35-41
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 35-41
+ v >>>= 7;
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 42-48
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 42-48
+ v >>>= 7;
+
+ if ((v & ~0x7FL) == 0L) {
+ putUncheckedByte((byte) v); // 49-55
+ return;
+ }
+ putUncheckedByte((byte) (v | 0x80L)); // 49-55
+ putUncheckedByte((byte) (v >>> 7)); // 56-63, last byte as is.
+ }
+
+ private void putUncheckedByte(byte i) {
+ unsafe.putByte(currentPosition, i);
+ ++currentPosition;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/EventWriterMethod.java b/src/share/classes/jdk/jfr/internal/EventWriterMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..f453b571793fbdb208e8349609a10570fe669aeb
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/EventWriterMethod.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import jdk.internal.org.objectweb.asm.commons.Method;
+import jdk.jfr.internal.EventInstrumentation.FieldInfo;
+
+public enum EventWriterMethod {
+
+ BEGIN_EVENT("(" + jdk.internal.org.objectweb.asm.Type.getType(PlatformEventType.class).getDescriptor() + ")Z", "???", "beginEvent"),
+ END_EVENT("()Z", "???", "endEvent"),
+ PUT_BYTE("(B)V", "byte", "putByte"),
+ PUT_SHORT("(S)V", "short", "putShort"),
+ PUT_INT("(I)V", "int", "putInt"),
+ PUT_LONG("(J)V", "long", "putLong"),
+ PUT_FLOAT("(F)V", "float", "putFloat"),
+ PUT_DOUBLE("(D)V", "double", "putDouble"),
+ PUT_CHAR("(C)V", "char", "putChar"),
+ PUT_BOOLEAN("(Z)V", "boolean", "putBoolean"),
+ PUT_THREAD("(Ljava/lang/Thread;)V", Type.THREAD.getName(), "putThread"),
+ PUT_CLASS("(Ljava/lang/Class;)V", Type.CLASS.getName(), "putClass"),
+ PUT_STRING("(Ljava/lang/String;Ljdk/jfr/internal/StringPool;)V", Type.STRING.getName(), "putString"),
+ PUT_EVENT_THREAD("()V", Type.THREAD.getName(), "putEventThread"),
+ PUT_STACK_TRACE("()V", Type.TYPES_PREFIX + "StackTrace", "putStackTrace");
+
+ private final Method asmMethod;
+ private final String typeDescriptor;
+
+ EventWriterMethod(String paramSignature, String typeName, String methodName) {
+ this.typeDescriptor = ASMToolkit.getDescriptor(typeName);
+ this.asmMethod = new Method(methodName, paramSignature);
+ }
+
+ public Method asASM() {
+ return asmMethod;
+ }
+
+ /**
+ * Return method in {@link EventWriter} class to use when writing event of
+ * a certain type.
+ *
+ * @param v field info
+ *
+ * @return the method
+ */
+ public static EventWriterMethod lookupMethod(FieldInfo v) {
+ // event thread
+ if (v.fieldName.equals(EventInstrumentation.FIELD_EVENT_THREAD)) {
+ return EventWriterMethod.PUT_EVENT_THREAD;
+ }
+ for (EventWriterMethod m : EventWriterMethod.values()) {
+ if (v.fieldDescriptor.equals(m.typeDescriptor)) {
+ return m;
+ }
+ }
+ throw new Error("Unknown type " + v.fieldDescriptor);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/JVM.java b/src/share/classes/jdk/jfr/internal/JVM.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c7baddebedc21a7ec44ced72d7a4af88c4f4db1
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/JVM.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.IOException;
+import java.util.List;
+
+import jdk.jfr.Event;
+
+/**
+ * Interface against the JVM.
+ *
+ */
+public final class JVM {
+ private static final JVM jvm = new JVM();
+
+ // JVM signals file changes by doing Object#notifu on this object
+ static final Object FILE_DELTA_CHANGE = new Object();
+
+ static final long RESERVED_CLASS_ID_LIMIT = 400;
+
+ private volatile boolean recording;
+ private volatile boolean nativeOK;
+
+ private static native void registerNatives();
+
+ static {
+ registerNatives();
+ // XXX
+ // for (LogTag tag : LogTag.values()) {
+ // subscribeLogLevel(tag, tag.id);
+ // }
+ Options.ensureInitialized();
+ }
+
+ /**
+ * Get the one and only JVM.
+ *
+ * @return the JVM
+ */
+ public static JVM getJVM() {
+ return jvm;
+ }
+
+ private JVM() {
+ }
+
+ /**
+ * Begin recording events
+ *
+ * Requires that JFR has been started with {@link #createNativeJFR()}
+ */
+ public native void beginRecording();
+
+ /**
+ * Return ticks
+ *
+ * @return the time, in ticks
+ *
+ */
+// @HotSpotIntrinsicCandidate
+ public static native long counterTime();
+
+
+ /**
+ * Emits native periodic event.
+ *
+ * @param eventTypeId type id
+ *
+ * @param timestamp commit time for event
+ * @param when when it is being done {@link Periodic.When}
+ *
+ * @return true if the event was committed
+ */
+ public native boolean emitEvent(long eventTypeId, long timestamp, long when);
+
+ /**
+ * End recording events, which includes flushing data in thread buffers
+ *
+ * Requires that JFR has been started with {@link #createNativeJFR()}
+ *
+ */
+ public native void endRecording();
+
+ /**
+ * Return a list of all classes deriving from {@link Event}
+ *
+ * @return list of event classes.
+ */
+ public native List> getAllEventClasses();
+
+ /**
+ * Return a count of the number of unloaded classes deriving from {@link Event}
+ *
+ * @return number of unloaded event classes.
+ */
+ public native long getUnloadedEventClassCount();
+
+ /**
+ * Return a unique identifier for a class. The class is marked as being
+ * "in use" in JFR.
+ *
+ * @param clazz clazz
+ *
+ * @return a unique class identifier
+ */
+// @HotSpotIntrinsicCandidate
+ public static native long getClassId(Class> clazz);
+
+ // temporary workaround until we solve intrinsics supporting epoch shift tagging
+ public static native long getClassIdNonIntrinsic(Class> clazz);
+
+ /**
+ * Return process identifier.
+ *
+ * @return process identifier
+ */
+ public native String getPid();
+
+ /**
+ * Return unique identifier for stack trace.
+ *
+ * Requires that JFR has been started with {@link #createNativeJFR()}
+ *
+ * @param skipCount number of frames to skip
+ * @return a unique stack trace identifier
+ */
+ public native long getStackTraceId(int skipCount);
+
+ /**
+ * Return identifier for thread
+ *
+ * @param t thread
+ * @return a unique thread identifier
+ */
+ public native long getThreadId(Thread t);
+
+ /**
+ * Frequency, ticks per second
+ *
+ * @return frequency
+ */
+ public native long getTicksFrequency();
+
+ /**
+ * Write message to log. Should swallow null or empty message, and be able
+ * to handle any Java character and not crash with very large message
+ *
+ * @param tagSetId the tagset id
+ * @param level on level
+ * @param message log message
+ *
+ */
+ public static native void log(int tagSetId, int level, String message);
+
+ /**
+ * Subscribe to LogLevel updates for LogTag
+ *
+ * @param lt the log tag to subscribe
+ * @param tagSetId the tagset id
+ */
+ public static native void subscribeLogLevel(LogTag lt, int tagSetId);
+
+ /**
+ * Call to invoke event tagging and retransformation of the passed classes
+ *
+ * @param classes
+ */
+ public native synchronized void retransformClasses(Class>[] classes);
+
+ /**
+ * Enable event
+ *
+ * @param eventTypeId event type id
+ *
+ * @param enabled enable event
+ */
+ public native void setEnabled(long eventTypeId, boolean enabled);
+
+ /**
+ * Interval at which the JVM should notify on {@link #FILE_DELTA_CHANGE}
+ *
+ * @param delta number of bytes, reset after file rotation
+ */
+ public native void setFileNotification(long delta);
+
+ /**
+ * Set the number of global buffers to use
+ *
+ * @param count
+ *
+ * @throws IllegalArgumentException if count is not within a valid range
+ * @throws IllegalStateException if value can't be changed
+ */
+ public native void setGlobalBufferCount(long count) throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * Set size of a global buffer
+ *
+ * @param size
+ *
+ * @throws IllegalArgumentException if buffer size is not within a valid
+ * range
+ */
+ public native void setGlobalBufferSize(long size) throws IllegalArgumentException;
+
+ /**
+ * Set overall memory size
+ *
+ * @param size
+ *
+ * @throws IllegalArgumentException if memory size is not within a valid
+ * range
+ */
+ public native void setMemorySize(long size) throws IllegalArgumentException;
+
+ /**
+
+ /**
+ * Set interval for method samples, in milliseconds.
+ *
+ * Setting interval to 0 turns off the method sampler.
+ *
+ * @param intervalMillis the sampling interval
+ */
+ public native void setMethodSamplingInterval(long type, long intervalMillis);
+
+ /**
+ * Sets the file where data should be written.
+ *
+ * Requires that JFR has been started with {@link #createNativeJFR()}
+ *
+ *
+ * Recording Previous Current Action
+ * ==============================================
+ * true null null Ignore, keep recording in-memory
+ * true null file1 Start disk recording
+ * true file null Copy out metadata to disk and continue in-memory recording
+ * true file1 file2 Copy out metadata and start with new File (file2)
+ * false * null Ignore, but start recording to memory with {@link #beginRecording()}
+ * false * file Ignore, but start recording to disk with {@link #beginRecording()}
+ *
+ *
+ *
+ * recording can be set to true/false with {@link #beginRecording()}
+ * {@link #endRecording()}
+ *
+ * @param file the file where data should be written, or null if it should
+ * not be copied out (in memory).
+ *
+ * @throws IOException
+ */
+ public native void setOutput(String file);
+
+ /**
+ * Controls if a class deriving from jdk.jfr.Event should
+ * always be instrumented on class load.
+ *
+ * @param force, true to force initialization, false otherwise
+ */
+ public native void setForceInstrumentation(boolean force);
+
+ /**
+ * Turn on/off thread sampling.
+ *
+ * @param sampleThreads true if threads should be sampled, false otherwise.
+ *
+ * @throws IllegalStateException if state can't be changed.
+ */
+ public native void setSampleThreads(boolean sampleThreads) throws IllegalStateException;
+
+ /**
+ * Turn on/off compressed integers.
+ *
+ * @param compressed true if compressed integers should be used, false
+ * otherwise.
+ *
+ * @throws IllegalStateException if state can't be changed.
+ */
+ public native void setCompressedIntegers(boolean compressed) throws IllegalStateException;
+
+ /**
+ * Set stack depth.
+ *
+ * @param depth
+ *
+ * @throws IllegalArgumentException if not within a valid range
+ * @throws IllegalStateException if depth can't be changed
+ */
+ public native void setStackDepth(int depth) throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * Turn on stack trace for an event
+ *
+ * @param eventTypeId the event id
+ *
+ * @param enabled if stack traces should be enabled
+ */
+ public native void setStackTraceEnabled(long eventTypeId, boolean enabled);
+
+ /**
+ * Set thread buffer size.
+ *
+ * @param size
+ *
+ * @throws IllegalArgumentException if size is not within a valid range
+ * @throws IllegalStateException if size can't be changed
+ */
+ public native void setThreadBufferSize(long size) throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * Set threshold for event,
+ *
+ * Long.MAXIMUM_VALUE = no limit
+ *
+ * @param eventTypeId the id of the event type
+ * @param ticks threshold in ticks,
+ * @return true, if it could be set
+ */
+ public native boolean setThreshold(long eventTypeId, long ticks);
+
+ /**
+ * Store the metadata descriptor that is to be written at the end of a
+ * chunk, data should be written after GMT offset and size of metadata event
+ * should be adjusted
+ *
+ * Requires that JFR has been started with {@link #createNativeJFR()}
+ *
+ * @param bytes binary representation of metadata descriptor
+ *
+ * @param binary representation of descriptor
+ */
+ public native void storeMetadataDescriptor(byte[] bytes);
+
+ public void endRecording_() {
+ endRecording();
+ recording = false;
+ }
+
+ public void beginRecording_() {
+ beginRecording();
+ recording = true;
+ }
+
+ public boolean isRecording() {
+ return recording;
+ }
+
+ /**
+ * If the JVM supports JVM TI and retransformation has not been disabled this
+ * method will return true. This flag can not change during the lifetime of
+ * the JVM.
+ *
+ * @return if transform is allowed
+ */
+ public native boolean getAllowedToDoEventRetransforms();
+
+ /**
+ * Set up native resources, data structures, threads etc. for JFR
+ *
+ * @param simulateFailure simulate a initialization failure and rollback in
+ * native, used for testing purposes
+ *
+ * @throws IllegalStateException if native part of JFR could not be created.
+ *
+ */
+ private native boolean createJFR(boolean simulateFailure) throws IllegalStateException;
+
+ /**
+ * Destroys native part of JFR. If already destroy, call is ignored.
+ *
+ * Requires that JFR has been started with {@link #createNativeJFR()}
+ *
+ * @return if an instance was actually destroyed.
+ *
+ */
+ private native boolean destroyJFR();
+
+ public boolean createFailedNativeJFR() throws IllegalStateException {
+ return createJFR(true);
+ }
+
+ public void createNativeJFR() {
+ nativeOK = createJFR(false);
+ }
+
+ public boolean destroyNativeJFR() {
+ boolean result = destroyJFR();
+ nativeOK = !result;
+ return result;
+ }
+
+ public boolean hasNativeJFR() {
+ return nativeOK;
+ }
+
+ /**
+ * Cheap test to check if JFR functionality is available.
+ *
+ * @return
+ */
+ public native boolean isAvailable();
+
+ /**
+ * To convert ticks to wall clock time.
+ */
+ public native double getTimeConversionFactor();
+
+ /**
+ * Return a unique identifier for a class. Compared to {@link #getClassId()}
+ * , this method does not tag the class as being "in-use".
+ *
+ * @param clazz class
+ *
+ * @return a unique class identifier
+ */
+ public native long getTypeId(Class> clazz);
+
+ /**
+ * Fast path fetching the EventWriter using VM intrinsics
+ *
+ * @return thread local EventWriter
+ */
+// @HotSpotIntrinsicCandidate
+ public static native Object getEventWriter();
+
+ /**
+ * Create a new EventWriter
+ *
+ * @return thread local EventWriter
+ */
+ public static native EventWriter newEventWriter();
+
+ /**
+ * Flushes the EventWriter for this thread.
+ */
+ public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize);
+
+ /**
+ * Sets the location of the disk repository, to be used at an emergency
+ * dump.
+ *
+ * @param dirText
+ */
+ public native void setRepositoryLocation(String dirText);
+
+ /**
+ * Access to VM termination support.
+ *
+ *@param errorMsg descriptive message to be include in VM termination sequence
+ */
+ public native void abort(String errorMsg);
+
+ /**
+ * Adds a string to the string constant pool.
+ *
+ * If the same string is added twice, two entries will be created.
+ *
+ * @param id identifier associated with the string, not negative
+ *
+ * @param s string constant to be added, not null
+ *
+ * @return the current epoch of this insertion attempt
+ */
+ public static native boolean addStringConstant(boolean epoch, long id, String s);
+ /**
+ * Gets the address of the jboolean epoch.
+ *
+ * The epoch alternates every checkpoint.
+ *
+ * @return The address of the jboolean.
+ */
+ public native long getEpochAddress();
+
+ public native void uncaughtException(Thread thread, Throwable t);
+ /**
+ * Sets cutoff for event.
+ *
+ * Determines how long the event should be allowed to run.
+ *
+ * Long.MAXIMUM_VALUE = no limit
+ *
+ * @param eventTypeId the id of the event type
+ * @param cutoffTicks cutoff in ticks,
+ * @return true, if it could be set
+ */
+ public native boolean setCutoff(long eventTypeId, long cutoffTicks);
+
+ /**
+ * Emit old object sample events.
+ *
+ * @param cutoff the cutoff in ticks
+ * @param emitAll emit all samples in old object queue
+ */
+ public native void emitOldObjectSamples(long cutoff, boolean emitAll);
+}
diff --git a/src/share/classes/jdk/jfr/internal/JVMSupport.java b/src/share/classes/jdk/jfr/internal/JVMSupport.java
new file mode 100644
index 0000000000000000000000000000000000000000..8203fc0c2797e3c61292e80c3d96502e6a3d7970
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/JVMSupport.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.internal;
+
+import java.io.IOException;
+
+/**
+ * Checks if the running VM supports Flight Recorder.
+ *
+ * Purpose of this helper class is to detect early and cleanly if the VM has
+ * support for Flight Recorder, i.e. not throw {@link UnsatisfiedLinkError} in
+ * unexpected places.
+ *
+ * This is needed so a disabled-jfr.jar can be built for non Oracle JDKs.
+ */
+public final class JVMSupport {
+
+ private static final String UNSUPPORTED_VM_MESSAGE = "Flight Recorder is not supported on this VM";
+ private static final boolean notAvailable = !checkAvailability();
+
+ private static boolean checkAvailability() {
+ // set jfr.unsupported.vm to true to test API on an unsupported VM
+ try {
+ if (SecuritySupport.getBooleanProperty("jfr.unsupported.vm")) {
+ return false;
+ }
+ } catch (NoClassDefFoundError cnfe) {
+ return false;
+ }
+ try {
+ // Will typically throw UnsatisfiedLinkError if
+ // there is no native implementation
+ JVM.getJVM().isAvailable();
+ return true;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public static void ensureWithInternalError() {
+ if (notAvailable) {
+ throw new InternalError(UNSUPPORTED_VM_MESSAGE);
+ }
+ }
+
+ public static void ensureWithIOException() throws IOException {
+ if (notAvailable) {
+ throw new IOException(UNSUPPORTED_VM_MESSAGE);
+ }
+ }
+
+ public static void ensureWithIllegalStateException() {
+ if (notAvailable) {
+ throw new IllegalStateException(UNSUPPORTED_VM_MESSAGE);
+ }
+ }
+
+ public static boolean isNotAvailable() {
+ return notAvailable;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/JVMUpcalls.java b/src/share/classes/jdk/jfr/internal/JVMUpcalls.java
new file mode 100644
index 0000000000000000000000000000000000000000..37071aafbd321f9e75c8d3259d480faeb617bb45
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/JVMUpcalls.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jfr.internal;
+
+import java.lang.reflect.Modifier;
+
+import jdk.jfr.Event;
+import jdk.jfr.internal.handlers.EventHandler;
+import jdk.jfr.internal.instrument.JDKEvents;
+
+/**
+ * All upcalls from the JVM should go through this class.
+ *
+ */
+// Called by native
+final class JVMUpcalls {
+ /**
+ * Called by the JVM when a retransform happens on a tagged class
+ *
+ * @param traceId
+ * Id of the class
+ * @param dummy
+ * (not used but needed since invoke infrastructure in native
+ * uses same signature bytesForEagerInstrumentation)
+ * @param clazz
+ * class being retransformed
+ * @param oldBytes
+ * byte code
+ * @return byte code to use
+ * @throws Throwable
+ */
+ static byte[] onRetransform(long traceId, boolean dummy, Class> clazz, byte[] oldBytes) throws Throwable {
+ try {
+ if (Event.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
+ EventHandler handler = Utils.getHandler(clazz.asSubclass(Event.class));
+ if (handler == null) {
+ Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "No event handler found for " + clazz.getName() + ". Ignoring instrumentation request.");
+ // Probably triggered by some other agent
+ return oldBytes;
+ }
+ Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding instrumentation to event class " + clazz.getName() + " using retransform");
+ EventInstrumentation ei = new EventInstrumentation(clazz.getSuperclass(), oldBytes, traceId);
+ byte[] bytes = ei.buildInstrumented();
+ ASMToolkit.logASM(clazz.getName(), bytes);
+ return bytes;
+ }
+ return JDKEvents.retransformCallback(clazz, oldBytes);
+ } catch (Throwable t) {
+ Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation to event class " + clazz.getName());
+ }
+ return oldBytes;
+
+ }
+
+ /**
+ * Called by the JVM when requested to do an "eager" instrumentation. Would
+ * normally happen when JVMTI retransform capabilities are not available.
+ *
+ * @param traceId
+ * Id of the class
+ * @param forceInstrumentation
+ * add instrumentation regardless if event is enabled or not.
+ * @param superClazz
+ * the super class of the class being processed
+ * @param oldBytes
+ * byte code
+ * @return byte code to use
+ * @throws Throwable
+ */
+ static byte[] bytesForEagerInstrumentation(long traceId, boolean forceInstrumentation, Class> superClass, byte[] oldBytes) throws Throwable {
+ if (JVMSupport.isNotAvailable()) {
+ return oldBytes;
+ }
+ String eventName = "";
+ try {
+ EventInstrumentation ei = new EventInstrumentation(superClass, oldBytes, traceId);
+ eventName = ei.getEventName();
+ if (!forceInstrumentation) {
+ // Assume we are recording
+ MetadataRepository mr = MetadataRepository.getInstance();
+ // No need to generate bytecode if:
+ // 1) Event class is disabled, and there is not an external configuration that overrides.
+ // 2) Event class has @Registered(false)
+ if (!mr.isEnabled(ei.getEventName()) && !ei.isEnabled() || !ei.isRegistered()) {
+ Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Skipping instrumentation for event type " + eventName + " since event was disabled on class load");
+ return oldBytes;
+ }
+ }
+ // Corner case when we are forced to generate bytecode. We can't reference the event
+ // handler in #isEnabled() before event class has been registered, so we add a
+ // guard against a null reference.
+ ei.setGuardHandler(true);
+ Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
+ EventHandlerCreator eh = new EventHandlerCreator(traceId, ei.getSettingInfos(), ei.getFieldInfos());
+ // Handler class must be loaded before instrumented event class can
+ // be used
+ eh.makeEventHandlerClass();
+ byte[] bytes = ei.buildInstrumented();
+ ASMToolkit.logASM(ei.getClassName() + "(" + traceId + ")", bytes);
+ return bytes;
+ } catch (Throwable t) {
+ Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Unexpected error when adding instrumentation for event type " + eventName);
+ return oldBytes;
+ }
+ }
+
+ /**
+ * Called by the JVM to create the recorder thread.
+ *
+ * @param systemThreadGroup
+ * the system thread group
+ *
+ * @param contextClassLoader
+ * the context class loader.
+ *
+ * @return a new thread
+ */
+ static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) {
+ return SecuritySupport.createRecorderThread(systemThreadGroup, contextClassLoader);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/LogLevel.java b/src/share/classes/jdk/jfr/internal/LogLevel.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb5af57a8576ae70e1e9f3d2825de18a39398f7d
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/LogLevel.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+public enum LogLevel {
+ TRACE(1),
+ DEBUG(2),
+ INFO(3),
+ WARN(4),
+ ERROR(5);
+ // must be in sync with JVM levels.
+
+ final int level;
+
+ LogLevel(int level) {
+ this.level = level;
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/LogTag.java b/src/share/classes/jdk/jfr/internal/LogTag.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6de8864af4e0438bdb6e6dde7ff06f330ce6cbd
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/LogTag.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+/* Mapped against c++ enum in jfrLogTagSet.hpp */
+public enum LogTag {
+ /**
+ * Covers
+ *
+ * Initialization of Flight Recorder
+ * recording life cycle (start, stop and dump)
+ * repository life cycle
+ * loading of configuration files.
+ *
+ * Target audience: operations
+ */
+ JFR(0),
+ /**
+ * Covers general implementation aspects of JFR (for Hotspot developers)
+ */
+ JFR_SYSTEM(1),
+ /**
+ * Covers JVM/JDK events (for Hotspot developers)
+ */
+ JFR_SYSTEM_EVENT(2),
+ /**
+ * Covers setting for the JVM/JDK (for Hotspot developers)
+ */
+ JFR_SYSTEM_SETTING(3),
+ /**
+ * Covers generated bytecode (for Hotspot developers)
+ */
+ JFR_SYSTEM_BYTECODE(4),
+ /**
+ * Covers XML parsing (for Hotspot developers)
+ */
+ JFR_SYSTEM_PARSER(5),
+ /**
+ * Covers metadata for JVM/JDK (for Hotspot developers)
+ */
+ JFR_SYSTEM_METADATA(6),
+ /**
+ * Covers metadata for Java user (for Hotspot developers)
+ */
+ JFR_METADATA(7),
+ /**
+ * Covers events (for users of the JDK)
+ */
+ JFR_EVENT(8),
+ /**
+ * Covers setting (for users of the JDK)
+ */
+ JFR_SETTING(9),
+ /**
+ * Covers usage of jcmd with JFR
+ */
+ JFR_DCMD(10);
+
+ /* set from native side */
+ private volatile int tagSetLevel = 100; // prevent logging if JVM log system has not been initialized
+
+ final int id;
+
+ LogTag(int tagId) {
+ id = tagId;
+ }
+
+ public boolean shouldLog(int level) {
+ return true;// XXX level >= tagSetLevel;
+ }
+
+ public boolean shouldLog(LogLevel logLevel) {
+ return shouldLog(logLevel.level);
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/Logger.java b/src/share/classes/jdk/jfr/internal/Logger.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab4bf421705a3c0a673d96d8c7e3cb6350262e9a
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/Logger.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.util.function.Supplier;
+
+/**
+ * JFR logger
+ *
+ */
+
+public final class Logger {
+
+ private final static int MAX_SIZE = 10000;
+
+ public static void log(LogTag logTag, LogLevel logLevel, String message) {
+ if (logTag.shouldLog(logLevel.level)) {
+ logInternal(logTag, logLevel, message);
+ }
+ }
+
+ public static void log(LogTag logTag, LogLevel logLevel, Supplier messageSupplier) {
+ if (logTag.shouldLog(logLevel.level)) {
+ logInternal(logTag, logLevel, messageSupplier.get());
+ }
+ }
+
+ private static void logInternal(LogTag logTag, LogLevel logLevel, String message) {
+ if (message == null || message.length() < MAX_SIZE) {
+ JVM.log(logTag.id, logLevel.level, message);
+ } else {
+ JVM.log(logTag.id, logLevel.level, message.substring(0, MAX_SIZE));
+ }
+ }
+}
diff --git a/src/share/classes/jdk/jfr/internal/MetadataDescriptor.java b/src/share/classes/jdk/jfr/internal/MetadataDescriptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..40da229b7eabbbd30e7f2b6aa51500ba2462add7
--- /dev/null
+++ b/src/share/classes/jdk/jfr/internal/MetadataDescriptor.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jfr.internal;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import jdk.jfr.EventType;
+
+/**
+ * Metadata about a chunk
+ */
+public final class MetadataDescriptor {
+
+ static final class Attribute {
+ final String name;
+ final String value;
+
+ private Attribute(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+ }
+
+ static final class Element {
+ final String name;
+ final List elements = new ArrayList<>();
+ final List attributes = new ArrayList<>();
+
+ Element(String name) {
+ this.name = name;
+ }
+
+ long longValue(String name) {
+ String v = attribute(name);
+ if (v != null)
+ return Long.parseLong(v);
+ else
+ throw new IllegalArgumentException(name);
+ }
+
+ String attribute(String name) {
+ for (Attribute a : attributes) {
+ if (a.name.equals(name)) {
+ return a.value;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ try {
+ prettyPrintXML(sb, "", this);
+ } catch (IOException e) {
+ // should not happen
+ }
+ return sb.toString();
+ }
+
+ long attribute(String name, long defaultValue) {
+ String text = attribute(name);
+ if (text == null) {
+ return defaultValue;
+ }
+ return Long.parseLong(text);
+ }
+
+ String attribute(String name, String defaultValue) {
+ String text = attribute(name);
+ if (text == null) {
+ return defaultValue;
+ }
+ return text;
+ }
+
+ List elements(String... names) {
+ List filteredElements = new ArrayList<>();
+ for (String name : names) {
+ for (Element e : elements) {
+ if (e.name.equals(name)) {
+ filteredElements.add(e);
+ }
+ }
+ }
+ return filteredElements;
+ }
+
+ void add(Element element) {
+ elements.add(element);
+ }
+
+ void addAttribute(String name, Object value) {
+ attributes.add(new Attribute(name, String.valueOf(value)));
+ }
+
+ Element newChild(String name) {
+ Element e = new Element(name);
+ elements.add(e);
+ return e;
+ }
+
+ public void addArrayAttribute(Element element, String name, Object value) {
+ String typeName = value.getClass().getComponentType().getName();
+ switch (typeName) {
+ case "int":
+ int[] ints = (int[]) value;
+ for (int i = 0; i < ints.length; i++) {
+ addAttribute(name + "-" + i , ints[i]);
+ }
+ break;
+ case "long":
+ long[] longs = (long[]) value;
+ for (int i = 0; i < longs.length; i++) {
+ addAttribute(name + "-" + i , longs[i]);
+ }
+ break;
+ case "float":
+ float[] floats = (float[]) value;
+ for (int i = 0; i < floats.length; i++) {
+ addAttribute(name + "-" + i , floats[i]);
+ }
+ break;
+
+ case "double":
+ double[] doubles = (double[]) value;
+ for (int i = 0; i < doubles.length; i++) {
+ addAttribute(name + "-" + i , doubles[i]);
+ }
+ break;
+ case "short":
+ short[] shorts = (short[]) value;
+ for (int i = 0; i < shorts.length; i++) {
+ addAttribute(name + "-" + i , shorts[i]);
+ }
+ break;
+ case "char":
+ char[] chars = (char[]) value;
+ for (int i = 0; i < chars.length; i++) {
+ addAttribute(name + "-" + i , chars[i]);
+ }
+ break;
+ case "byte":
+ byte[] bytes = (byte[]) value;
+ for (int i = 0; i < bytes.length; i++) {
+ addAttribute(name + "-" + i , bytes[i]);
+ }
+ break;
+ case "boolean":
+ boolean[] booleans = (boolean[]) value;
+ for (int i = 0; i < booleans.length; i++) {
+ addAttribute(name + "-" + i , booleans[i]);
+ }
+ break;
+ case "java.lang.String":
+ String[] strings = (String[]) value;
+ for (int i = 0; i < strings.length; i++) {
+ addAttribute(name + "-" + i , strings[i]);
+ }
+ break;
+ default:
+ throw new InternalError("Array type of " + typeName + " is not supported");
+ }
+ }
+ }
+
+ static final String ATTRIBUTE_ID = "id";
+ static final String ATTRIBUTE_SIMPLE_TYPE = "simpleType";
+ static final String ATTRIBUTE_GMT_OFFSET = "gmtOffset";
+ static final String ATTRIBUTE_LOCALE = "locale";
+ static final String ELEMENT_TYPE = "class";
+ static final String ELEMENT_SETTING = "setting";
+ static final String ELEMENT_ANNOTATION = "annotation";
+ static final String ELEMENT_FIELD = "field";
+ static final String ATTRIBUTE_SUPER_TYPE = "superType";
+ static final String ATTRIBUTE_TYPE_ID = "class";
+ static final String ATTRIBUTE_DIMENSION = "dimension";
+ static final String ATTRIBUTE_NAME = "name";
+ static final String ATTRIBUTE_CONSTANT_POOL = "constantPool";
+ static final String ATTRIBUTE_DEFAULT_VALUE = "defaultValue";
+
+ final List eventTypes = new ArrayList<>();
+ final Collection types = new ArrayList<>();
+ long gmtOffset;
+ String locale;
+ Element root;
+
+ // package private
+ MetadataDescriptor() {
+ }
+
+ private static void prettyPrintXML(Appendable sb, String indent, Element e) throws IOException {
+ sb.append(indent + "<" + e.name);
+ for (Attribute a : e.attributes) {
+ sb.append(" ").append(a.name).append("=\"").append(a.value).append("\"");
+ }
+ if (e.elements.size() == 0) {
+ sb.append("/");
+ }
+ sb.append(">\n");
+ for (Element child : e.elements) {
+ prettyPrintXML(sb, indent + " ", child);
+ }
+ if (e.elements.size() != 0) {
+ sb.append(indent).append("").append(e.name).append(">\n");
+ }
+ }
+
+ public Collection getTypes() {
+ return types;
+ }
+
+ public List getEventTypes() {
+ return eventTypes;
+ }
+
+ public int getGMTOffset() {
+ return (int) gmtOffset;
+ }
+
+ public String getLocale() {
+ return locale;
+ }
+
+ public static MetadataDescriptor read(DataInput input) throws IOException {
+ MetadataReader r = new MetadataReader(input);
+ return r.getDescriptor();
+ }
+
+ static void write(List